#include <sdk.h>
#include <prep.h>
#include <wx/filedlg.h>
#include <wx/propgrid/propgrid.h>
#include <wx/xml/xml.h>
//#include "advancedcompileroptionsdlg.h"
#include "compilergcc.h"
#include "compileroptionsdlg.h"
#include "debuggermanager.h"
#include "editpathdlg.h"
#include "editpairdlg.h"
#include "compilerflagdlg.h"

BEGIN_EVENT_TABLE( CompilerOptionsDlg, wxPanel )
  EVT_UPDATE_UI( XRCID( "btnEditDir" ), CompilerOptionsDlg::OnUpdateUI )
  EVT_UPDATE_UI( XRCID( "btnDelDir" ), CompilerOptionsDlg::OnUpdateUI )
  EVT_UPDATE_UI( XRCID( "btnClearDir" ), CompilerOptionsDlg::OnUpdateUI )
  EVT_UPDATE_UI( XRCID( "btnCopyDirs" ), CompilerOptionsDlg::OnUpdateUI )
  EVT_UPDATE_UI( XRCID( "btnMoveDirUp" ), CompilerOptionsDlg::OnUpdateUI )
  EVT_UPDATE_UI( XRCID( "btnMoveDirDown" ), CompilerOptionsDlg::OnUpdateUI )
  EVT_UPDATE_UI( XRCID( "btnEditVar" ), CompilerOptionsDlg::OnUpdateUI )
  EVT_UPDATE_UI( XRCID( "btnDeleteVar" ), CompilerOptionsDlg::OnUpdateUI )
  EVT_UPDATE_UI( XRCID( "btnClearVar" ), CompilerOptionsDlg::OnUpdateUI )
  EVT_UPDATE_UI( XRCID( "cmbCompilerPolicy" ), CompilerOptionsDlg::OnUpdateUI )
  EVT_UPDATE_UI( XRCID( "cmbLinkerPolicy" ), CompilerOptionsDlg::OnUpdateUI )
  EVT_UPDATE_UI( XRCID( "cmbIncludesPolicy" ), CompilerOptionsDlg::OnUpdateUI )
  EVT_UPDATE_UI( XRCID( "cmbLibDirsPolicy" ), CompilerOptionsDlg::OnUpdateUI )
  EVT_UPDATE_UI( XRCID( "btnSetDefaultCompiler" ), CompilerOptionsDlg::OnUpdateUI )
  EVT_UPDATE_UI( XRCID( "btnAddCompiler" ), CompilerOptionsDlg::OnUpdateUI )
  EVT_UPDATE_UI( XRCID( "btnRenameCompiler" ), CompilerOptionsDlg::OnUpdateUI )
  EVT_UPDATE_UI( XRCID( "btnDelCompiler" ), CompilerOptionsDlg::OnUpdateUI )
  EVT_UPDATE_UI( XRCID( "btnResetCompiler" ), CompilerOptionsDlg::OnUpdateUI )
  EVT_UPDATE_UI( XRCID( "txtMasterPath" ), CompilerOptionsDlg::OnUpdateUI )
  EVT_UPDATE_UI( XRCID( "btnMasterPath" ), CompilerOptionsDlg::OnUpdateUI )
  EVT_UPDATE_UI( XRCID( "btnExtraAdd" ), CompilerOptionsDlg::OnUpdateUI )
  EVT_UPDATE_UI( XRCID( "btnExtraEdit" ), CompilerOptionsDlg::OnUpdateUI )
  EVT_UPDATE_UI( XRCID( "btnExtraDelete" ), CompilerOptionsDlg::OnUpdateUI )
  EVT_UPDATE_UI( XRCID( "btnExtraClear" ), CompilerOptionsDlg::OnUpdateUI )
  EVT_UPDATE_UI( XRCID( "txtCcompiler" ), CompilerOptionsDlg::OnUpdateUI )
  EVT_UPDATE_UI( XRCID( "btnCcompiler" ), CompilerOptionsDlg::OnUpdateUI )
  EVT_UPDATE_UI( XRCID( "txtCPPcompiler" ), CompilerOptionsDlg::OnUpdateUI )
  EVT_UPDATE_UI( XRCID( "btnCPPcompiler" ), CompilerOptionsDlg::OnUpdateUI )
  EVT_UPDATE_UI( XRCID( "txtLinker" ), CompilerOptionsDlg::OnUpdateUI )
  EVT_UPDATE_UI( XRCID( "btnLinker" ), CompilerOptionsDlg::OnUpdateUI )
  EVT_UPDATE_UI( XRCID( "txtLibLinker" ), CompilerOptionsDlg::OnUpdateUI )
  EVT_UPDATE_UI( XRCID( "btnLibLinker" ), CompilerOptionsDlg::OnUpdateUI )
  EVT_UPDATE_UI( XRCID( "cmbDebugger" ), CompilerOptionsDlg::OnUpdateUI )
  EVT_UPDATE_UI( XRCID( "txtMake" ), CompilerOptionsDlg::OnUpdateUI )
  EVT_UPDATE_UI( XRCID( "btnMake" ), CompilerOptionsDlg::OnUpdateUI )
  EVT_UPDATE_UI( XRCID( "cmbCompiler" ), CompilerOptionsDlg::OnUpdateUI )
  EVT_UPDATE_UI( XRCID( "btnIgnoreAdd" ), CompilerOptionsDlg::OnUpdateUI )
  EVT_UPDATE_UI( XRCID( "btnIgnoreRemove" ), CompilerOptionsDlg::OnUpdateUI )
  EVT_TREE_SEL_CHANGED( XRCID( "tcScope" ), CompilerOptionsDlg::OnTreeSelectionChange )
  EVT_TREE_SEL_CHANGING( XRCID( "tcScope" ), CompilerOptionsDlg::OnTreeSelectionChanging )
  EVT_CHOICE( XRCID( "cmbCategory" ), CompilerOptionsDlg::OnCategoryChanged )
  EVT_CHOICE( XRCID( "cmbCompiler" ), CompilerOptionsDlg::OnCompilerChanged )

  EVT_CHOICE( XRCID( "lstCommands" ), CompilerOptionsDlg::OnCommandsChange )  // 命令行
  EVT_CHOICE( XRCID( "lstExt" ), CompilerOptionsDlg::OnExtChange )  // 扩展
  EVT_BUTTON( XRCID( "btnAddExt" ), CompilerOptionsDlg::OnAddExt )  // 添加扩展
  EVT_BUTTON( XRCID( "btnRemoveExt" ), CompilerOptionsDlg::OnDelExt )  // 移除扩展
  EVT_LISTBOX( XRCID( "lstRegex" ), CompilerOptionsDlg::OnRegexChange )  // 解析表达式
  EVT_BUTTON( XRCID( "btnRegexTest" ), CompilerOptionsDlg::OnRegexTest )
  EVT_BUTTON( XRCID( "btnRegexAdd" ), CompilerOptionsDlg::OnRegexAdd )
  EVT_BUTTON( XRCID( "btnRegexDelete" ), CompilerOptionsDlg::OnRegexDelete )
  EVT_BUTTON( XRCID( "btnRegexDefaults" ), CompilerOptionsDlg::OnRegexDefaults )
  EVT_SPIN_UP( XRCID( "spnRegexOrder" ), CompilerOptionsDlg::OnRegexUp )
  EVT_SPIN_DOWN( XRCID( "spnRegexOrder" ), CompilerOptionsDlg::OnRegexDown )

  EVT_LISTBOX_DCLICK( XRCID( "lstVars" ), CompilerOptionsDlg::OnEditVarClick )
  EVT_BUTTON( XRCID( "btnSetDefaultCompiler" ), CompilerOptionsDlg::OnSetDefaultCompilerClick )
  EVT_BUTTON( XRCID( "btnAddCompiler" ), CompilerOptionsDlg::OnAddCompilerClick )
  EVT_BUTTON( XRCID( "btnRenameCompiler" ), CompilerOptionsDlg::OnEditCompilerClick )
  EVT_BUTTON( XRCID( "btnDelCompiler" ), CompilerOptionsDlg::OnRemoveCompilerClick )
  EVT_BUTTON( XRCID( "btnResetCompiler" ), CompilerOptionsDlg::OnResetCompilerClick )
  EVT_BUTTON( XRCID( "btnAddDir" ), CompilerOptionsDlg::OnAddDirClick )
  EVT_BUTTON( XRCID( "btnEditDir" ), CompilerOptionsDlg::OnEditDirClick )
  EVT_LISTBOX_DCLICK( XRCID( "lstIncludeDirs" ), CompilerOptionsDlg::OnEditDirClick )
  EVT_LISTBOX_DCLICK( XRCID( "lstLibDirs" ), CompilerOptionsDlg::OnEditDirClick )
  EVT_BUTTON( XRCID( "btnDelDir" ), CompilerOptionsDlg::OnRemoveDirClick )
  EVT_BUTTON( XRCID( "btnClearDir" ), CompilerOptionsDlg::OnClearDirClick )
  EVT_BUTTON( XRCID( "btnCopyDirs" ), CompilerOptionsDlg::OnCopyDirsClick )
  EVT_LISTBOX_DCLICK( XRCID( "lstExtraPaths" ), CompilerOptionsDlg::OnEditExtraPathClick )
  EVT_BUTTON( XRCID( "btnExtraAdd" ), CompilerOptionsDlg::OnAddExtraPathClick )
  EVT_BUTTON( XRCID( "btnExtraEdit" ), CompilerOptionsDlg::OnEditExtraPathClick )
  EVT_BUTTON( XRCID( "btnExtraDelete" ), CompilerOptionsDlg::OnRemoveExtraPathClick )
  EVT_BUTTON( XRCID( "btnExtraClear" ), CompilerOptionsDlg::OnClearExtraPathClick )
  EVT_BUTTON( XRCID( "btnAddVar" ), CompilerOptionsDlg::OnAddVarClick )
  EVT_BUTTON( XRCID( "btnEditVar" ), CompilerOptionsDlg::OnEditVarClick )
  EVT_BUTTON( XRCID( "btnDeleteVar" ), CompilerOptionsDlg::OnRemoveVarClick )
  EVT_BUTTON( XRCID( "btnClearVar" ), CompilerOptionsDlg::OnClearVarClick )
  EVT_BUTTON( XRCID( "btnMasterPath" ), CompilerOptionsDlg::OnMasterPathClick )
  EVT_BUTTON( XRCID( "btnAutoDetect" ), CompilerOptionsDlg::OnAutoDetectClick )
  EVT_BUTTON( XRCID( "btnCcompiler" ), CompilerOptionsDlg::OnSelectProgramClick )
  EVT_BUTTON( XRCID( "btnCPPcompiler" ), CompilerOptionsDlg::OnSelectProgramClick )
  EVT_BUTTON( XRCID( "btnLinker" ), CompilerOptionsDlg::OnSelectProgramClick )
  EVT_BUTTON( XRCID( "btnLibLinker" ), CompilerOptionsDlg::OnSelectProgramClick )
  EVT_BUTTON( XRCID( "btnMake" ), CompilerOptionsDlg::OnSelectProgramClick )
  EVT_BUTTON( XRCID( "btnAdvanced" ), CompilerOptionsDlg::OnAdvancedClick )
  EVT_BUTTON( XRCID( "btnIgnoreAdd" ), CompilerOptionsDlg::OnIgnoreAddClick )
  EVT_BUTTON( XRCID( "btnIgnoreRemove" ), CompilerOptionsDlg::OnIgnoreRemoveClick )
  EVT_CHOICE( XRCID( "cmbCompilerPolicy" ), CompilerOptionsDlg::OnDirty )
  EVT_CHOICE( XRCID( "cmbLinkerPolicy" ), CompilerOptionsDlg::OnDirty )
  EVT_CHOICE( XRCID( "cmbIncludesPolicy" ), CompilerOptionsDlg::OnDirty )
  EVT_CHOICE( XRCID( "cmbLibDirsPolicy" ), CompilerOptionsDlg::OnDirty )
  EVT_CHOICE( XRCID( "cmbLogging" ), CompilerOptionsDlg::OnDirty )
  EVT_CHECKBOX( XRCID( "chkAlwaysRunPost" ), CompilerOptionsDlg::OnDirty )
  EVT_CHECKBOX( XRCID( "chkNonPlatComp" ), CompilerOptionsDlg::OnDirty )
  EVT_TEXT( XRCID( "txtCompilerOptions" ), CompilerOptionsDlg::OnDirty )
  EVT_TEXT( XRCID( "txtResourceCompilerOptions" ), CompilerOptionsDlg::OnDirty )
  EVT_TEXT( XRCID( "txtCompilerDefines" ), CompilerOptionsDlg::OnDirty )
  EVT_TEXT( XRCID( "txtLinkerOptions" ), CompilerOptionsDlg::OnDirty )
  EVT_TEXT( XRCID( "txtCmdBefore" ), CompilerOptionsDlg::OnDirty )
  EVT_TEXT( XRCID( "txtCmdAfter" ), CompilerOptionsDlg::OnDirty )
  EVT_TEXT( XRCID( "txtMasterPath" ), CompilerOptionsDlg::OnDirty )
  EVT_TEXT( XRCID( "txtCcompiler" ), CompilerOptionsDlg::OnDirty )
  EVT_TEXT( XRCID( "txtCPPcompiler" ), CompilerOptionsDlg::OnDirty )
  EVT_TEXT( XRCID( "txtLinker" ), CompilerOptionsDlg::OnDirty )
  EVT_TEXT( XRCID( "txtLibLinker" ), CompilerOptionsDlg::OnDirty )
  EVT_TEXT( XRCID( "txtMake" ), CompilerOptionsDlg::OnDirty )
  EVT_TEXT( XRCID( "txtMakeCmd_Build" ), CompilerOptionsDlg::OnDirty )
  EVT_TEXT( XRCID( "txtMakeCmd_Compile" ), CompilerOptionsDlg::OnDirty )
  EVT_TEXT( XRCID( "txtMakeCmd_Clean" ), CompilerOptionsDlg::OnDirty )
  EVT_TEXT( XRCID( "txtMakeCmd_AskRebuildNeeded" ), CompilerOptionsDlg::OnDirty )
  EVT_CHAR_HOOK( CompilerOptionsDlg::OnMyCharHook )
  EVT_PG_CHANGED( XRCID( "pgCompilerFlags" ), CompilerOptionsDlg::OnOptionChanged )
  EVT_PG_RIGHT_CLICK( XRCID( "pgCompilerFlags" ), CompilerOptionsDlg::OnFlagsPopup )
  EVT_PG_DOUBLE_CLICK( XRCID( "pgCompilerFlags" ), CompilerOptionsDlg::OnOptionDoubleClick )
END_EVENT_TABLE()

inline wxString ControlCharsToString( const wxString& src ) {
  wxString ret = src;
  ret.Replace( _T( "\t" ), _T( "\\t" ) );
  ret.Replace( _T( "\n" ), _T( "\\n" ) );
  ret.Replace( _T( "\r" ), _T( "\\r" ) );
  ret.Replace( _T( "\a" ), _T( "\\a" ) );
  ret.Replace( _T( "\b" ), _T( "\\b" ) );
  return ret;
}

wxString StringToControlChars( const wxString& src ) {
  wxString ret = src;
  ret.Replace( _T( "\\t" ), _T( "\t" ) );
  ret.Replace( _T( "\\n" ), _T( "\n" ) );
  ret.Replace( _T( "\\r" ), _T( "\r" ) );
  ret.Replace( _T( "\\a" ), _T( "\a" ) );
  ret.Replace( _T( "\\b" ), _T( "\b" ) );
  return ret;
}

class ScopeTreeData : public wxTreeItemData {
  public:
    ScopeTreeData( M_Code_Project* project, ProjectBuildTarget* target ) { m_Project = project; m_Target = target; }
    M_Code_Project* GetProject() { return m_Project; }
    ProjectBuildTarget* GetTarget() { return m_Target; }
  private:
    M_Code_Project* m_Project;
    ProjectBuildTarget* m_Target;
};

struct DebuggerClientData : wxClientData {
  DebuggerClientData( const wxString &s ) : string( s ) {}
  wxString string;
};

struct VariableListClientData : wxClientData {
  VariableListClientData( const wxString &key, const wxString &value ) : key( key ), value( value ) {}
  wxString key, value;
};

CompilerOptionsDlg::CompilerOptionsDlg( wxWindow* parent, CompilerGCC* compiler, M_Code_Project* project, ProjectBuildTarget* target ) :
  m_FlagsPG( nullptr ),
  m_Compiler( compiler ),
  m_CurrentCompilerIdx( 0 ),
  m_pProject( project ),
  m_pTarget( target ),
  m_bDirty( false ),
  m_BuildingTree( false ) {
  wxXmlResource* xml_res = wxXmlResource::Get();
  wxString resPath = ConfigManager::GetDataFolder() + _T( "/配置编译器参数.xrc" );
  if( !xml_res->Load( resPath ) )  {
    xml_File_lock = false;
    Manager::Get()->GetLogManager()->LogInf( F( _T( "文件丢失: %s " ), resPath.wx_str() ) );
    return;
  }
  if( !xml_res->LoadPanel( this, parent, _T( "dlgCompilerOptions" ) ) ) {
    xml_File_lock = false;
    Manager::Get()->GetLogManager()->LogInf( _T( "Dlg 资源丢失" ) );
    return;
  }
  xml_File_lock = true;
  m_FlagsPG = new wxPropertyGrid( this, XRCID( "pgCompilerFlags" ), wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL | wxPG_SPLITTER_AUTO_CENTER );
  m_FlagsPG->SetExtraStyle( wxPG_EX_HELP_AS_TOOLTIPS );
  m_FlagsPG->SetColumnProportion( 0, 70 );
  m_FlagsPG->SetColumnProportion( 1, 30 );
  m_FlagsPG->SetMinSize( wxSize( 400, 400 ) );
  wxXmlResource::Get()->AttachUnknownControl( _T( "pgCompilerFlags" ), m_FlagsPG );
  if( m_pProject ) {
    bool hasBuildScripts = m_pProject->GetBuildScripts().GetCount() != 0;
    if( !hasBuildScripts ) {
      for( int x = 0; x < m_pProject->GetBuildTargetsCount(); ++x ) {
        ProjectBuildTarget* curr_target = m_pProject->GetBuildTarget( x );
        hasBuildScripts = curr_target->GetBuildScripts().GetCount() != 0;
        if( hasBuildScripts ) {
          break;
        }
      }
    }
    XRCCTRL( *this, "lblBuildScriptsNote", wxStaticText )->Show( hasBuildScripts );
  }
  wxTreeCtrl* tree = XRCCTRL( *this, "tcScope", wxTreeCtrl );
  wxSizer* sizer = tree->GetContainingSizer();
  wxNotebook* nb = XRCCTRL( *this, "nbMain", wxNotebook );
  if( !m_pProject ) {
    SetLabel( _T( "编译器设置" ) );
    sizer->Show( tree, false );
    sizer->Detach( tree );
    nb->DeletePage( 6 );
    nb->DeletePage( 3 );
  } else {
    nb->DeletePage( 8 );
    nb->DeletePage( 7 );
    nb->DeletePage( 4 );
    wxWindow* win = XRCCTRL( *this, "btnAddCompiler", wxButton );
    wxSizer* sizer2 = win->GetContainingSizer();
    sizer2->Clear( true );
    sizer2->RecalcSizes();
    sizer2->Layout();
    bool en = project->IsMakefileCustom();
    XRCCTRL( *this, "txtMakeCmd_Build", wxTextCtrl )->Enable( en );
    XRCCTRL( *this, "txtMakeCmd_Compile", wxTextCtrl )->Enable( en );
    XRCCTRL( *this, "txtMakeCmd_Clean", wxTextCtrl )->Enable( en );
    XRCCTRL( *this, "txtMakeCmd_DistClean", wxTextCtrl )->Enable( en );
    XRCCTRL( *this, "txtMakeCmd_AskRebuildNeeded", wxTextCtrl )->Enable( en );
    XRCCTRL( *this, "txtMakeCmd_SilentBuild", wxTextCtrl )->Enable( en );
  }
  DoFillOthers();
  DoFillTree();
  int compilerIdx = CompilerFactory::GetCompilerIndex( CompilerFactory::GetDefaultCompilerID() );
  if( m_pTarget ) {
    compilerIdx = CompilerFactory::GetCompilerIndex( m_pTarget->GetCompilerID() );
  } else if( m_pProject ) {
    compilerIdx = CompilerFactory::GetCompilerIndex( m_pProject->GetCompilerID() );
  }
  if( ( m_pTarget || m_pProject ) && compilerIdx == -1 ) {
    wxString CompilerId = m_pTarget ? m_pTarget->GetCompilerID() : m_pProject->GetCompilerID();
    wxString msg;
    msg.Printf( _T( "找不到定义的编译器 (ID: %s).\n"
                    "Please choose the compiler you want to use instead and click \"OK\".\n"
                    "If you click \"Cancel\", the project/target will remain configured for\n"
                    "that compiler and consequently can not be configured and will not be built." ),
                CompilerId.wx_str() );
    Compiler* comp = 0;
    if( ( m_pTarget ) || ( !m_pTarget && m_pProject ) ) {
      comp = CompilerFactory::SelectCompilerUI( msg );
    }
    if( comp ) {
      int NewCompilerIdx = CompilerFactory::GetCompilerIndex( comp );
      DoFillCompilerSets( NewCompilerIdx );
      wxCommandEvent Dummy;
      OnCompilerChanged( Dummy );
    } else {
      DoFillCompilerSets( compilerIdx );
      if( nb ) {
        nb->Disable();
      }
    }
  } else {
    if( !CompilerFactory::GetCompiler( compilerIdx ) ) {
      compilerIdx = 0;
    }
    DoFillCompilerSets( compilerIdx );
    m_Options = CompilerFactory::GetCompiler( compilerIdx )->GetOptions();
    m_CurrentCompilerIdx = compilerIdx;
    DoFillCompilerDependentSettings();
  }
  if( m_pTarget && m_pTarget->GetTargetType() == ttCommandsOnly ) {
    nb->GetPage( 0 )->Disable();
    nb->GetPage( 1 )->Disable();
    nb->GetPage( 2 )->Disable();
    nb->GetPage( 5 )->Disable();
    nb->SetSelection( 3 );
  } else {
    nb->SetSelection( 0 );
  }
  sizer->Layout();
  Layout();
  GetSizer()->Layout();
  GetSizer()->SetSizeHints( this );
  this->SetSize( -1, -1, 0, 0 );
  if( project && project->IsMakefileCustom() ) {
    nb->RemovePage( 2 );
    nb->RemovePage( 1 );
    nb->RemovePage( 0 );
    XRCCTRL( *this, "tabCompiler", wxPanel )->Show( false );
    XRCCTRL( *this, "tabLinker", wxPanel )->Show( false );
    XRCCTRL( *this, "tabDirs", wxPanel )->Show( false );
  }
  //CheckCompilerOptions();
  m_bDirty = false;
  Fit();
}

CompilerOptionsDlg::~CompilerOptionsDlg() {
}

void CompilerOptionsDlg::CheckCompilerOptions( bool type ) {
  if( xml_File_lock ) {
    Compiler* compiler = CompilerFactory::GetCompiler( m_CompilerId );
    if( !compiler ) {
      return;
    }
    wxChoice* lst = XRCCTRL( *this, "lstCommands", wxChoice );  // 命令行
    lst->Clear();
    for( int i = 0; i < ctCount; ++i ) {
      m_Commands[i] = compiler->GetCommandToolsVector( ( CommandType )i );
      lst->Append( Compiler::CommandTypeDescriptions[i] );
    }
    wxTextCtrl* addincludepath = XRCCTRL( *this, "txtAddIncludePath", wxTextCtrl );
    wxTextCtrl* addlibpath = XRCCTRL( *this, "txtAddLibPath", wxTextCtrl );
    wxTextCtrl* addlib = XRCCTRL( *this, "txtAddLib", wxTextCtrl );
    wxTextCtrl* libprefix = XRCCTRL( *this, "txtLibPrefix", wxTextCtrl );
    wxTextCtrl* libext = XRCCTRL( *this, "txtLibExt", wxTextCtrl );
    wxTextCtrl* define = XRCCTRL( *this, "txtDefine", wxTextCtrl );
    wxTextCtrl* genericswitch = XRCCTRL( *this, "txtGenericSwitch", wxTextCtrl );
    wxTextCtrl* objectext = XRCCTRL( *this, "txtObjectExt", wxTextCtrl );
    wxCheckBox* fwdslashes = XRCCTRL( *this, "chkFwdSlashes", wxCheckBox );
    wxCheckBox* linkerneedslibprefix = XRCCTRL( *this, "chkLinkerNeedsLibPrefix", wxCheckBox );
    wxCheckBox* linkerneedslibext = XRCCTRL( *this, "chkLinkerNeedsLibExt", wxCheckBox );
    wxCheckBox* linkerneedspathres = XRCCTRL( *this, "chkLinkerNeedsPathRes", wxCheckBox );
    wxCheckBox* needdeps = XRCCTRL( *this, "chkNeedDeps", wxCheckBox );
    wxCheckBox* forcecompilerquotes = XRCCTRL( *this, "chkForceCompilerQuotes", wxCheckBox );
    wxCheckBox* forcelinkerquotes = XRCCTRL( *this, "chkForceLinkerQuotes", wxCheckBox );
    wxCheckBox* supportspch = XRCCTRL( *this, "chkSupportsPCH", wxCheckBox );
    wxTextCtrl* pchext = XRCCTRL( *this, "txtPCHExt", wxTextCtrl );
    wxCheckBox* useflatobjects = XRCCTRL( *this, "chkUseFlatObjects", wxCheckBox );
    wxCheckBox* usefullsourcepaths = XRCCTRL( *this, "chkUseFullSourcePaths", wxCheckBox );
    wxTextCtrl* includedirseparator = XRCCTRL( *this, "txtIncludeDirSeparator", wxTextCtrl );
    wxTextCtrl* libdirseparator = XRCCTRL( *this, "txtLibDirSeparator", wxTextCtrl );
    wxTextCtrl* objectseparator = XRCCTRL( *this, "txtObjectSeparator", wxTextCtrl );
    wxSpinCtrl* statussuccess = XRCCTRL( *this, "spnStatusSuccess", wxSpinCtrl );
    if( type ) { // Write
      CompilerSwitches switches;
      switches.includeDirs = addincludepath->GetValue();
      switches.libDirs = addlibpath->GetValue();
      switches.linkLibs = addlib->GetValue();
      switches.libPrefix = libprefix->GetValue();
      switches.libExtension = libext->GetValue();
      switches.defines = define->GetValue();
      switches.genericSwitch = genericswitch->GetValue();
      switches.objectExtension = objectext->GetValue();
      switches.forceFwdSlashes = fwdslashes->GetValue();
      switches.linkerNeedsLibPrefix = linkerneedslibprefix->GetValue();
      switches.linkerNeedsLibExtension = linkerneedslibext->GetValue();
      switches.linkerNeedsPathResolved = linkerneedspathres->GetValue();
      switches.needDependencies = needdeps->GetValue();
      switches.forceCompilerUseQuotes  = forcecompilerquotes->GetValue();
      switches.forceLinkerUseQuotes = forcelinkerquotes->GetValue();
      switches.supportsPCH = supportspch->GetValue();
      switches.PCHExtension = pchext->GetValue();
      switches.UseFlatObjects = useflatobjects->GetValue();
      switches.UseFullSourcePaths = usefullsourcepaths->GetValue();
      wxString value = includedirseparator->GetValue();
      if( !value.IsEmpty() ) {
        switches.includeDirSeparator = value[0];
      }
      value = libdirseparator->GetValue();
      if( !value.IsEmpty() ) {
        switches.libDirSeparator = value[0];
      }
      value = objectseparator->GetValue();
      if( !value.IsEmpty() ) {
        switches.objectSeparator = value[0];
      }
      switches.statusSuccess = statussuccess->GetValue();
      compiler->SetSwitches( switches );
    } else { // Read
      lst->SetSelection( 0 );
      DisplayCommand( 0, 0 );
      ReadExtensions( 0 );
      const CompilerSwitches& switches = compiler->GetSwitches();
      addincludepath->SetValue( switches.includeDirs );
      addlibpath->SetValue( switches.libDirs );
      addlib->SetValue( switches.linkLibs );
      libprefix->SetValue( switches.libPrefix );
      libext->SetValue( switches.libExtension );
      define->SetValue( switches.defines );
      genericswitch->SetValue( switches.genericSwitch );
      objectext->SetValue( switches.objectExtension );
      fwdslashes->SetValue( switches.forceFwdSlashes );
      linkerneedslibprefix->SetValue( switches.linkerNeedsLibPrefix );
      linkerneedslibext->SetValue( switches.linkerNeedsLibExtension );
      linkerneedspathres->SetValue( switches.linkerNeedsPathResolved );
      needdeps->SetValue( switches.needDependencies );
      forcecompilerquotes->SetValue( switches.forceCompilerUseQuotes );
      forcelinkerquotes->SetValue( switches.forceLinkerUseQuotes );
      supportspch->SetValue( switches.supportsPCH );
      pchext->SetValue( switches.PCHExtension );
      useflatobjects->SetValue( switches.UseFlatObjects );
      usefullsourcepaths->SetValue( switches.UseFullSourcePaths );
      includedirseparator->SetValue( switches.includeDirSeparator );
      libdirseparator->SetValue( switches.libDirSeparator );
      objectseparator->SetValue( switches.objectSeparator );
      statussuccess->SetValue( switches.statusSuccess );
      m_Regexes = compiler->GetRegExArray();
      m_SelectedRegex = m_Regexes.size() > 0 ? 0 : -1;
      FillRegexes();
    }
  }
}

void CompilerOptionsDlg::ReadExtensions( int nr ) {
  wxChoice* cmb = XRCCTRL( *this, "lstExt", wxChoice );  // 扩展
  cmb->Clear();
  for( size_t i = 0; i < m_Commands[nr].size(); ++i ) {
    cmb->Append( GetStringFromArray( m_Commands[nr][i].extensions, DEFAULT_ARRAY_SEP, false ) );
  }
  cmb->SetSelection( cmb->FindString( wxEmptyString ) );
}

CompilerTool* CompilerOptionsDlg::GetCompilerTool( int cmd, int ext ) {
  const wxChoice* cmb = XRCCTRL( *this, "lstExt", wxChoice );  // 扩展
  for( size_t i = 0; i < m_Commands[cmd].size(); ++i ) {
    if( cmb->GetString( ext ).IsEmpty() && m_Commands[cmd][i].extensions.GetCount() == 0 ) {
      return &m_Commands[cmd][i];
    }
    int selExt = m_Commands[cmd][i].extensions.Index( cmb->GetString( ext ) );
    if( selExt != -1 ) {
      return &m_Commands[cmd][i];
    }
  }
  return 0;
}

void CompilerOptionsDlg::DisplayCommand( int cmd, int ext ) {
  wxTextCtrl* text = XRCCTRL( *this, "txtCommand",   wxTextCtrl );  //宏命令行
  wxTextCtrl* gen  = XRCCTRL( *this, "txtGenerated", wxTextCtrl );  //生成文件编译命令
  if( CompilerTool* tool = GetCompilerTool( cmd, ext ) ) {
    text->SetValue( tool->command );
    gen->SetValue( GetStringFromArray( tool->generatedFiles, _T( "\n" ), false ) );
  } else {
    text->Clear();
    gen->Clear();
  }
  m_LastCmdIndex = cmd;
  m_LastExtIndex = ext;
}

void CompilerOptionsDlg::CheckForChanges() {
  if( m_LastCmdIndex != -1 && m_LastExtIndex != -1 ) {
    SaveCommands( m_LastCmdIndex, m_LastExtIndex );
  }
}

void CompilerOptionsDlg::SaveCommands( int cmd, int ext ) {
  if( cmd == -1 || ext == -1 ) {
    return;
  }
  if( CompilerTool* tool = GetCompilerTool( cmd, ext ) ) {
    wxTextCtrl* text = XRCCTRL( *this, "txtCommand",   wxTextCtrl );  //宏命令行
    wxTextCtrl* gen  = XRCCTRL( *this, "txtGenerated", wxTextCtrl );  //生成文件编译命令
    if( text->GetValue() != tool->command ) {
      tool->command = text->GetValue();
    }
    wxString gens = GetStringFromArray( tool->generatedFiles, _T( "\n" ), false );
    if( gen->GetValue() != gens ) {
      tool->generatedFiles = GetArrayFromString( gen->GetValue(), _T( "\n" ) );
    }
  }
}

void CompilerOptionsDlg::FillRegexes() {
  wxListBox* Regexlist = XRCCTRL( *this, "lstRegex", wxListBox );  // 解析表达式
  Regexlist->Clear();
  for( size_t i = 0; i < m_Regexes.size(); ++i ) {
    RegExStruct& rs = m_Regexes[i];
    Regexlist->Append( rs.desc );
  }
  Regexlist->SetSelection( m_SelectedRegex );
  FillRegexDetails( m_SelectedRegex );
}

void CompilerOptionsDlg::FillRegexDetails( int index ) {  // 子表达式索引
  if( xml_File_lock ) {
    wxTextCtrl* RegexDesc = XRCCTRL( *this, "txtRegexDesc", wxTextCtrl ); // 描述
    wxComboBox* RegexType = XRCCTRL( *this, "cmbRegexType", wxComboBox ); // 类型
    wxTextCtrl* Regex = XRCCTRL( *this, "txtRegex", wxTextCtrl ); // 规则表达式
    wxSpinCtrl* RegexMsg1 = XRCCTRL( *this, "spnRegexMsg1", wxSpinCtrl ); // 消息
    wxSpinCtrl* RegexMsg2 = XRCCTRL( *this, "spnRegexMsg2", wxSpinCtrl ); // 附加消息1
    wxSpinCtrl* RegexMsg3 = XRCCTRL( *this, "spnRegexMsg3", wxSpinCtrl ); // 附加消息2
    wxSpinCtrl* RegexFilename = XRCCTRL( *this, "spnRegexFilename", wxSpinCtrl ); // 文件名
    wxSpinCtrl* RegexLine = XRCCTRL( *this, "spnRegexLine", wxSpinCtrl ); // 行
    if( index != -1 ) {
      RegExStruct& rs = m_Regexes[index];
      RegexDesc->SetValue( rs.desc );
      RegexType->SetSelection( ( int )rs.lt );
      Regex->SetValue( ControlCharsToString( rs.GetRegExString() ) );
      RegexMsg1->SetValue( rs.msg[0] );
      RegexMsg2->SetValue( rs.msg[1] );
      RegexMsg3->SetValue( rs.msg[2] );
      RegexFilename->SetValue( rs.filename );
      RegexLine->SetValue( rs.line );
    } else {
      RegexDesc->SetValue( _T( "" ) );
      RegexType->SetSelection( -1 );
      Regex->SetValue( _T( "" ) );
      RegexMsg1->SetValue( 0 );
      RegexMsg2->SetValue( 0 );
      RegexMsg3->SetValue( 0 );
      RegexFilename->SetValue( 0 );
      RegexLine->SetValue( 0 );
    }
  }
}

void CompilerOptionsDlg::SaveRegexDetails( int index ) {
  if( index == -1 ) {
    return;
  }
  RegExStruct& rs = m_Regexes[index];
  rs.desc = XRCCTRL( *this, "txtRegexDesc", wxTextCtrl )->GetValue();
  rs.lt = ( CompilerLineType )XRCCTRL( *this, "cmbRegexType", wxComboBox )->GetSelection();
  rs.SetRegExString( StringToControlChars( XRCCTRL( *this, "txtRegex",  wxTextCtrl )->GetValue() ) );
  rs.msg[0] = XRCCTRL( *this, "spnRegexMsg1", wxSpinCtrl )->GetValue();
  rs.msg[1] = XRCCTRL( *this, "spnRegexMsg2", wxSpinCtrl )->GetValue();
  rs.msg[2] = XRCCTRL( *this, "spnRegexMsg3", wxSpinCtrl )->GetValue();
  rs.filename = XRCCTRL( *this, "spnRegexFilename", wxSpinCtrl )->GetValue();
  rs.line = XRCCTRL( *this, "spnRegexLine", wxSpinCtrl )->GetValue();
}

void CompilerOptionsDlg::OnCommandsChange( wxCommandEvent & ( event ) ) {
  CheckForChanges();
  int cmd = XRCCTRL( *this, "lstCommands", wxChoice )->GetSelection();  // 命令行
  ReadExtensions( cmd );
  int ext = XRCCTRL( *this, "lstExt", wxChoice )->GetSelection();  // 扩展
  DisplayCommand( cmd, ext );
}

void CompilerOptionsDlg::OnExtChange( wxCommandEvent & ( event ) ) {
  CheckForChanges();
  int cmd = XRCCTRL( *this, "lstCommands", wxChoice )->GetSelection();  // 命令行
  int ext = XRCCTRL( *this, "lstExt", wxChoice )->GetSelection();  // 扩展
  DisplayCommand( cmd, ext );
}

void CompilerOptionsDlg::OnAddExt( wxCommandEvent & ( event ) ) {
  wxString ext = cbGetTextFromUser( _T( "输入以分号分隔的扩展名列表:" ), _T( "新扩展" ) );
  ext.Trim( false );
  ext.Trim( true );
  if( !ext.IsEmpty() ) {
    int nr = XRCCTRL( *this, "lstCommands", wxChoice )->GetSelection();  // 命令行
    CompilerTool* ptool = GetCompilerTool( nr, 0 );
    CompilerTool tool( ptool ? ptool->command : wxString(), ext );
    m_Commands[nr].push_back( tool );
    ReadExtensions( nr );
    wxChoice* cmb = XRCCTRL( *this, "lstExt", wxChoice );  // 扩展
    cmb->SetStringSelection( ext );
    DisplayCommand( nr, cmb->GetSelection() );
  }
}

void CompilerOptionsDlg::OnDelExt( wxCommandEvent & ( event ) ) {
  if( msging( _T( "是否确实要从列表中删除此扩展集?" ), _T( "Confirmation" ), wxYES_NO, this ) == wxID_YES ) {
    int nr = XRCCTRL( *this, "lstCommands", wxChoice )->GetSelection();  // 命令行
    wxChoice* cmb = XRCCTRL( *this, "lstExt", wxChoice );  // 扩展
    wxString extS = cmb->GetStringSelection();
    if( !extS.IsEmpty() ) {
      int ext = cmb->GetSelection();
      m_Commands[nr].erase( m_Commands[nr].begin() + ext );
      ReadExtensions( nr );
      cmb->SetSelection( 0 );
      m_LastExtIndex = -1;
      DisplayCommand( nr, 0 );
    } else {
      msging( _T( "无法删除默认命令!" ), _T( "错误" ), wxICON_ERROR, this );
    }
  }
}

void CompilerOptionsDlg::OnRegexChange( wxCommandEvent & ( event ) ) {
  wxListBox* Regexlist = XRCCTRL( *this, "lstRegex", wxListBox );  // 解析表达式
  if( Regexlist->GetSelection() == wxNOT_FOUND ) {
    return;
  }
  SaveRegexDetails( m_SelectedRegex );
  Regexlist->SetString( m_SelectedRegex, XRCCTRL( *this, "txtRegexDesc", wxTextCtrl )->GetValue() );
  m_SelectedRegex = Regexlist->GetSelection();
  FillRegexDetails( m_SelectedRegex );
}

void CompilerOptionsDlg::OnRegexAdd( wxCommandEvent & ( event ) ) {
  SaveRegexDetails( m_SelectedRegex );
  m_Regexes.push_back( RegExStruct( _T( "新建正则表达式" ), cltError, _T( "" ), 0 ) );
  m_SelectedRegex = m_Regexes.size() - 1;
  FillRegexes();
}

void CompilerOptionsDlg::OnRegexDelete( wxCommandEvent & ( event ) ) {
  if( msging( _T( "是否确实要删除此正则表达式?" ), _T( "审查" ), wxICON_QUESTION | wxYES_NO | wxNO_DEFAULT, this ) == wxID_YES ) {
    m_Regexes.erase( ( m_Regexes.begin() + m_SelectedRegex ) );
    if( m_SelectedRegex >= ( int )m_Regexes.size() ) {
      --m_SelectedRegex;
    }
    FillRegexes();
  }
}

void CompilerOptionsDlg::OnRegexDefaults( wxCommandEvent & ( event ) ) {
  if( msging( _T( "重置此编译器的默认正则表达式 所有正则表达式都将被删除并替换为它们的默认对应项" ),
              _T( "审查" ), wxICON_QUESTION | wxYES_NO | wxNO_DEFAULT, this ) == wxID_YES ) {
    Compiler* compiler = CompilerFactory::GetCompiler( m_CompilerId );
    if( !compiler ) {
      return;
    }
    compiler->LoadDefaultRegExArray( true );
    m_Regexes = compiler->GetRegExArray();
    while( m_SelectedRegex >= ( int )m_Regexes.size() ) {
      --m_SelectedRegex;
    }
    FillRegexes();
  }
}

void CompilerOptionsDlg::OnRegexUp( wxSpinEvent & ( event ) ) {
  if( m_SelectedRegex <= 0 ) {
    return;
  }
  SaveRegexDetails( m_SelectedRegex );
  RegExStruct rs = m_Regexes[m_SelectedRegex];
  m_Regexes.erase( m_Regexes.begin() + m_SelectedRegex );
  m_Regexes.insert( ( m_Regexes.begin() + ( m_SelectedRegex - 1 ) ), rs );
  --m_SelectedRegex;
  FillRegexes();
}

void CompilerOptionsDlg::OnRegexDown( wxSpinEvent & ( event ) ) {
  if( m_SelectedRegex >= ( int )m_Regexes.size() - 1 ) {
    return;
  }
  SaveRegexDetails( m_SelectedRegex );
  RegExStruct rs = m_Regexes[m_SelectedRegex];
  m_Regexes.erase( m_Regexes.begin() + m_SelectedRegex );
  m_Regexes.insert( ( m_Regexes.begin() + ( m_SelectedRegex + 1 ) ), rs );
  ++m_SelectedRegex;
  FillRegexes();
}

void CompilerOptionsDlg::OnRegexTest( wxCommandEvent & ( event ) ) {
  if( m_SelectedRegex == -1 ) {
    return;
  }
  wxString text = XRCCTRL( *this, "txtRegexTest", wxTextCtrl )->GetValue();
  if( text.IsEmpty() ) {
    msging( _T( "Please enter a compiler line in the \"Compiler output\" text box..." ), _T( "错误" ), wxICON_ERROR, this );
    return;
  }
  Compiler* compiler = CompilerFactory::GetCompiler( m_CompilerId );
  if( !compiler ) {
    return;
  }
  RegExArray regex_copy = m_Regexes;
  SaveRegexDetails( m_SelectedRegex );
  compiler->SetRegExArray( m_Regexes );
  CompilerLineType clt = compiler->CheckForWarningsAndErrors( text );
  compiler->SetRegExArray( regex_copy );
  m_Regexes = regex_copy;
  wxString msg;
  msg.Printf( _T( "Regular expression analyzed as follows:\n\n"
                  "Type: %s message\n"
                  "Filename: %s\n"
                  "Line number: %s\n"
                  "Message: %s" ),
              clt == cltNormal ? _T( "Normal" ) : ( clt == cltInfo   ? _T( "Info" )
                  : ( clt == cltError  ? _T( "错误" ) : _T( "警告" ) ) ),
              compiler->GetLastErrorFilename().wx_str(),
              compiler->GetLastErrorLine().wx_str(),
              compiler->GetLastError().wx_str()
            );
  msging( msg, _T( "Test results" ), wxICON_INFORMATION, this );
}

void CompilerOptionsDlg::EndModal( int retCode ) {
  if( retCode == wxID_OK ) {
    m_bDirty = true;
    Compiler* compiler = CompilerFactory::GetCompiler( m_CompilerId );
    CheckForChanges();
    //CheckCompilerOptions(true);
    SaveRegexDetails( m_SelectedRegex );
    if( compiler ) {
      compiler->SetRegExArray( m_Regexes );
    }
  }
  //  wxScrollingDialog::EndModal( retCode );
}



void CompilerOptionsDlg::DoFillCompilerSets( int compilerIdx ) {
  wxChoice* cmb = XRCCTRL( *this, "cmbCompiler", wxChoice );
  cmb->Clear();
  for( unsigned int i = 0; i < CompilerFactory::GetCompilersCount(); ++i ) {
    cmb->Append( CompilerFactory::GetCompiler( i )->GetName() );
  }
  if( compilerIdx != -1 ) {
    cmb->SetSelection( compilerIdx );
  }
}

void CompilerOptionsDlg::DoFillCompilerDependentSettings() {
  DoFillCompilerPrograms();
  DoLoadOptions();
  DoFillVars();
  m_bDirty = false;
  m_bFlagsDirty = false;
}

void CompilerOptionsDlg::DoSaveCompilerDependentSettings() {
  DoSaveCompilerPrograms();
  DoSaveOptions();
  DoSaveVars();
  if( m_bFlagsDirty ) {
    DoSaveCompilerDefinition();
  }
  ProjectTargetCompilerAdjust();
  m_bDirty = false;
  m_bFlagsDirty = false;
}

inline void ArrayString2ListBox( const wxArrayString& array, wxListBox* control ) {
  control->Clear();
  int count = array.GetCount();
  for( int i = 0; i < count; ++i ) {
    if( !array[i].IsEmpty() ) {
      control->Append( array[i] );
    }
  }
}

inline void ListBox2ArrayString( wxArrayString& array, const wxListBox* control ) {
  array.Clear();
  int count = control->GetCount();
  for( int i = 0; i < count; ++i ) {
    wxString tmp = control->GetString( i );
    if( !tmp.IsEmpty() ) {
      array.Add( tmp );
    }
  }
}

void CompilerOptionsDlg::DoFillCompilerPrograms() {
  if( m_pProject ) {
    return;
  }
  const Compiler* compiler = CompilerFactory::GetCompiler( m_CurrentCompilerIdx );
  if( !compiler ) {
    return;
  }
  const CompilerPrograms& progs = compiler->GetPrograms();
  XRCCTRL( *this, "txtMasterPath", wxTextCtrl )->SetValue( compiler->GetMasterPath() );
  XRCCTRL( *this, "txtCcompiler", wxTextCtrl )->SetValue( progs.C );
  XRCCTRL( *this, "txtCPPcompiler", wxTextCtrl )->SetValue( progs.CPP );
  XRCCTRL( *this, "txtLinker", wxTextCtrl )->SetValue( progs.LD );
  XRCCTRL( *this, "txtLibLinker", wxTextCtrl )->SetValue( progs.LIB );
  wxChoice *cmbDebugger = XRCCTRL( *this, "cmbDebugger", wxChoice );
  if( cmbDebugger ) {
    cmbDebugger->Clear();
    cmbDebugger->Append( _T( "--- Invalid debugger ---" ), new DebuggerClientData( progs.DBGconfig ) );
    cmbDebugger->SetSelection( 0 );
    const DebuggerManager::RegisteredPlugins &plugins = Manager::Get()->GetDebuggerManager()->GetAllDebuggers();
    for( DebuggerManager::RegisteredPlugins::const_iterator it = plugins.begin(); it != plugins.end(); ++it ) {
      const DebuggerManager::PluginData &data = it->second;
      for( DebuggerManager::ConfigurationVector::const_iterator itConf = data.GetConfigurations().begin();
           itConf != data.GetConfigurations().end();
           ++itConf ) {
        const wxString &def = it->first->GetSettingsName() + _T( ":" ) + ( *itConf )->GetName();
        int index = cmbDebugger->Append( it->first->GetGUIName() + _T( " : " ) + ( *itConf )->GetName(),
                                         new DebuggerClientData( def ) );
        if( def == progs.DBGconfig ) {
          cmbDebugger->SetSelection( index );
        }
      }
    }
  }
  XRCCTRL( *this, "txtMake", wxTextCtrl )->SetValue( progs.MAKE );
  const wxArrayString& extraPaths = compiler->GetExtraPaths();
  ArrayString2ListBox( extraPaths, XRCCTRL( *this, "lstExtraPaths", wxListBox ) );
}

void CompilerOptionsDlg::DoFillVars() {
  wxListBox* lst = XRCCTRL( *this, "lstVars", wxListBox );
  if( !lst ) {
    return;
  }
  lst->Clear();
  const StringHash* vars = 0;
  const CompileOptionsBase* base = GetVarsOwner();
  if( base ) {
    vars = &base->GetAllVars();
  }
  if( !vars ) {
    return;
  }
  for( StringHash::const_iterator it = vars->begin(); it != vars->end(); ++it ) {
    wxString text = it->first + _T( " = " ) + it->second;
    lst->Append( text, new VariableListClientData( it->first, it->second ) );
  }
}

void CompilerOptionsDlg::DoFillOthers() {
  if( m_pProject ) {
    return;
  }
  wxCheckBox* chk = XRCCTRL( *this, "chkIncludeFileCwd", wxCheckBox );
  if( chk ) {
    chk->SetValue( Manager::Get()->GetConfigManager( _T( "compiler" ) )->ReadBool( _T( "/include_file_cwd" ), false ) );
  }
  chk = XRCCTRL( *this, "chkIncludePrjCwd", wxCheckBox );
  if( chk ) {
    chk->SetValue( Manager::Get()->GetConfigManager( _T( "compiler" ) )->ReadBool( _T( "/include_prj_cwd" ), false ) );
  }
  chk = XRCCTRL( *this, "chkSkipIncludeDeps", wxCheckBox );
  if( chk ) {
    chk->SetValue( Manager::Get()->GetConfigManager( _T( "compiler" ) )->ReadBool( _T( "/skip_include_deps" ), false ) );
  }
  chk = XRCCTRL( *this, "chkSaveHtmlLog", wxCheckBox );
  if( chk ) {
    chk->SetValue( Manager::Get()->GetConfigManager( _T( "compiler" ) )->ReadBool( _T( "/save_html_build_log" ), false ) );
  }
  chk = XRCCTRL( *this, "chkFullHtmlLog", wxCheckBox );
  if( chk ) {
    chk->SetValue( Manager::Get()->GetConfigManager( _T( "compiler" ) )->ReadBool( _T( "/save_html_build_log/full_command_line" ), false ) );
  }
  wxSpinCtrl* spn = XRCCTRL( *this, "spnParallelProcesses", wxSpinCtrl );
  if( spn ) {
    spn->SetValue( Manager::Get()->GetConfigManager( _T( "compiler" ) )->ReadInt( _T( "/parallel_processes" ), 2 ) );
  }
  spn = XRCCTRL( *this, "spnMaxErrors", wxSpinCtrl );
  if( spn ) {
    spn->SetRange( 0, 1000 );
    spn->SetValue( Manager::Get()->GetConfigManager( _T( "compiler" ) )->ReadInt( _T( "/max_reported_errors" ), 50 ) );
  }
  chk = XRCCTRL( *this, "chkRebuildSeperately", wxCheckBox );
  if( chk ) {
    chk->SetValue( Manager::Get()->GetConfigManager( _T( "compiler" ) )->ReadBool( _T( "/rebuild_seperately" ), false ) );
  }
  wxListBox* lst = XRCCTRL( *this, "lstIgnore", wxListBox );
  if( lst ) {
    wxArrayString IgnoreOutput;
    IgnoreOutput = Manager::Get()->GetConfigManager( _T( "compiler" ) )->ReadArrayString( _T( "/ignore_output" ) );
    ArrayString2ListBox( IgnoreOutput, lst );
  }
  chk = XRCCTRL( *this, "chkNonPlatComp", wxCheckBox );
  if( chk ) {
    chk->SetValue( Manager::Get()->GetConfigManager( _T( "compiler" ) )->ReadBool( _T( "/non_plat_comp" ), false ) );
  }
}

void CompilerOptionsDlg::DoFillTree() {
  m_BuildingTree = true;
  wxTreeCtrl* tc = XRCCTRL( *this, "tcScope", wxTreeCtrl );
  tc->DeleteAllItems();
  wxTreeItemId root;
  wxTreeItemId selectedItem;
  if( !m_pProject ) {
    root = tc->AddRoot( _T( "Global options" ), -1, -1 );
    selectedItem = root;
  } else {
    ScopeTreeData* data = new ScopeTreeData( m_pProject, 0L );
    root = tc->AddRoot( m_pProject->GetTitle(), -1, -1, data );
    selectedItem = root;
    for( int x = 0; x < m_pProject->GetBuildTargetsCount(); ++x ) {
      ProjectBuildTarget* target = m_pProject->GetBuildTarget( x );
      data = new ScopeTreeData( m_pProject, target );
      wxTreeItemId targetItem = tc->AppendItem( root, target->GetTitle(), -1, -1, data );
      if( target == m_pTarget ) {
        selectedItem = targetItem;
      }
    }
  }
  if( selectedItem == root ) {
    m_pTarget = 0;
  }
  tc->Expand( root );
  tc->SelectItem( selectedItem );
  m_BuildingTree = false;
}

void CompilerOptionsDlg::DoFillOptions() {
  m_FlagsPG->Freeze();
  m_FlagsPG->Clear();
  typedef std::map<wxString, wxPropertyCategory*> MapCategories;
  MapCategories categories;
  for( size_t i = 0; i < m_Options.GetCount(); ++i ) {
    const CompOption* option = m_Options.GetOption( i );
    if( option->category == _T( "General" ) ) {
      wxPropertyCategory *categoryProp = new wxPropertyCategory( option->category );
      m_FlagsPG->Append( categoryProp );
      categories[option->category] = categoryProp;
      break;
    }
  }
  for( size_t i = 0; i < m_Options.GetCount(); ++i ) {
    const CompOption* option = m_Options.GetOption( i );
    wxPropertyCategory *categoryProp = nullptr;
    MapCategories::iterator itCat = categories.find( option->category );
    if( itCat != categories.end() ) {
      categoryProp = itCat->second;
    } else {
      categoryProp = new wxPropertyCategory( option->category );
      m_FlagsPG->Append( categoryProp );
      categories[option->category] = categoryProp;
    }
    wxPGProperty *prop = new wxBoolProperty( option->name, wxPG_LABEL, option->enabled );
    m_FlagsPG->AppendIn( categoryProp, prop );
    m_FlagsPG->SetPropertyAttribute( prop, wxPG_BOOL_USE_CHECKBOX, true, wxPG_RECURSE );
  }
  wxPGProperty *root = m_FlagsPG->GetRoot();
  if( root ) {
    unsigned count = root->GetChildCount();
    for( unsigned ii = 0; ii < count; ++ii ) {
      m_FlagsPG->Sort( root->Item( ii ) );
    }
  }
  m_FlagsPG->Thaw();
}

void CompilerOptionsDlg::TextToOptions() {
  for( size_t n = 0; n < m_Options.GetCount(); ++n ) {
    if( CompOption* copt = m_Options.GetOption( n ) ) {
      copt->enabled = false;
    }
  }
  wxString rest;
  Compiler* compiler = CompilerFactory::GetCompiler( m_CurrentCompilerIdx );
  XRCCTRL( *this, "txtCompilerDefines", wxTextCtrl )->Clear();
  unsigned int i = 0;
  while( i < m_CompilerOptions.GetCount() ) {
    wxString opt = m_CompilerOptions.Item( i );
    opt = opt.Strip( wxString::both );
    CompOption* copt = m_Options.GetOptionByOption( opt );
    if( copt ) {
      copt->enabled = true;
      m_CompilerOptions.RemoveAt( i, 1 );
    } else if( compiler && opt.StartsWith( compiler->GetSwitches().defines, &rest ) ) {
      XRCCTRL( *this, "txtCompilerDefines", wxTextCtrl )->AppendText( rest );
      XRCCTRL( *this, "txtCompilerDefines", wxTextCtrl )->AppendText( _T( "\n" ) );
      m_CompilerOptions.RemoveAt( i, 1 );
    } else {
      ++i;
    }
  }
  i = 0;
  while( i < m_LinkerOptions.GetCount() ) {
    wxString opt = m_LinkerOptions.Item( i );
    opt = opt.Strip( wxString::both );
    CompOption* copt = m_Options.GetOptionByAdditionalLibs( opt );
    if( copt ) {
      copt->enabled = true;
      m_LinkerOptions.RemoveAt( i, 1 );
    } else {
      ++i;
    }
  }
}

inline void ArrayString2TextCtrl( const wxArrayString& array, wxTextCtrl* control ) {
  control->Clear();
  int count = array.GetCount();
  for( int i = 0; i < count; ++i ) {
    if( !array[i].IsEmpty() ) {
      control->AppendText( array[i] );
      control->AppendText( _T( '\n' ) );
    }
  }
}

inline void DoGetCompileOptions( wxArrayString& array, const wxTextCtrl* control ) {
  array.Clear();
  wxString tmp = control->GetValue();
  int nl = tmp.Find( _T( '\n' ) );
  wxString line;
  if( nl == -1 ) {
    line = tmp;
    tmp = _T( "" );
  } else {
    line = tmp.Left( nl );
  }
  while( nl != -1 || !line.IsEmpty() ) {
    if( !line.IsEmpty() ) {
      line.Replace( _T( "\r" ), _T( " " ), true );
      line.Replace( _T( "\n" ), _T( " " ), true );
      array.Add( line.Strip( wxString::both ) );
    }
    tmp.Remove( 0, nl + 1 );
    nl = tmp.Find( _T( '\n' ) );
    if( nl == -1 ) {
      line = tmp;
      tmp = _T( "" );
    } else {
      line = tmp.Left( nl );
    }
  }
}

void CompilerOptionsDlg::DoLoadOptions() {
  wxArrayString CommandsBeforeBuild;
  wxArrayString CommandsAfterBuild;
  bool AlwaysUsePost = false;
  wxArrayString IncludeDirs;
  wxArrayString LibDirs;
  wxArrayString ResDirs;
  if( !m_pProject && !m_pTarget ) {
    const Compiler* compiler = CompilerFactory::GetCompiler( m_CurrentCompilerIdx );
    if( compiler ) {
      IncludeDirs = compiler->GetIncludeDirs();
      ResDirs = compiler->GetResourceIncludeDirs();
      LibDirs = compiler->GetLibDirs();
      m_CompilerOptions = compiler->GetCompilerOptions();
      m_ResourceCompilerOptions = compiler->GetResourceCompilerOptions();
      m_LinkerOptions = compiler->GetLinkerOptions();
      m_LinkLibs = compiler->GetLinkLibs();
      wxChoice* cmb = XRCCTRL( *this, "cmbLogging", wxChoice );
      if( cmb ) {
        cmb->SetSelection( ( int )compiler->GetSwitches().logging );
      }
    }
  } else {
    if( !m_pTarget ) {
      SetLabel( _T( "Project build options" ) );
      IncludeDirs = m_pProject->GetIncludeDirs();
      ResDirs = m_pProject->GetResourceIncludeDirs();
      LibDirs = m_pProject->GetLibDirs();
      m_CompilerOptions = m_pProject->GetCompilerOptions();
      m_ResourceCompilerOptions = m_pProject->GetResourceCompilerOptions();
      m_LinkerOptions = m_pProject->GetLinkerOptions();
      m_LinkLibs = m_pProject->GetLinkLibs();
      CommandsAfterBuild = m_pProject->GetCommandsAfterBuild();
      CommandsBeforeBuild = m_pProject->GetCommandsBeforeBuild();
      AlwaysUsePost = m_pProject->GetAlwaysRunPostBuildSteps();
      XRCCTRL( *this, "txtMakeCmd_Build", wxTextCtrl )->SetValue( m_pProject->GetMakeCommandFor( mcBuild ) );
      XRCCTRL( *this, "txtMakeCmd_Compile", wxTextCtrl )->SetValue( m_pProject->GetMakeCommandFor( mcCompileFile ) );
      XRCCTRL( *this, "txtMakeCmd_Clean", wxTextCtrl )->SetValue( m_pProject->GetMakeCommandFor( mcClean ) );
      XRCCTRL( *this, "txtMakeCmd_DistClean", wxTextCtrl )->SetValue( m_pProject->GetMakeCommandFor( mcDistClean ) );
      XRCCTRL( *this, "txtMakeCmd_AskRebuildNeeded", wxTextCtrl )->SetValue( m_pProject->GetMakeCommandFor( mcAskRebuildNeeded ) );
      XRCCTRL( *this, "txtMakeCmd_SilentBuild", wxTextCtrl )->SetValue( m_pProject->GetMakeCommandFor( mcSilentBuild ) );
    } else {
      SetLabel( _T( "Target build options: " ) + m_pTarget->GetTitle() );
      IncludeDirs = m_pTarget->GetIncludeDirs();
      ResDirs = m_pTarget->GetResourceIncludeDirs();
      LibDirs = m_pTarget->GetLibDirs();
      m_CompilerOptions = m_pTarget->GetCompilerOptions();
      m_ResourceCompilerOptions = m_pTarget->GetResourceCompilerOptions();
      m_LinkerOptions = m_pTarget->GetLinkerOptions();
      m_LinkLibs = m_pTarget->GetLinkLibs();
      CommandsAfterBuild = m_pTarget->GetCommandsAfterBuild();
      CommandsBeforeBuild = m_pTarget->GetCommandsBeforeBuild();
      AlwaysUsePost = m_pTarget->GetAlwaysRunPostBuildSteps();
      XRCCTRL( *this, "cmbCompilerPolicy", wxChoice )->SetSelection( m_pTarget->GetOptionRelation( ortCompilerOptions ) );
      XRCCTRL( *this, "cmbLinkerPolicy", wxChoice )->SetSelection( m_pTarget->GetOptionRelation( ortLinkerOptions ) );
      XRCCTRL( *this, "cmbIncludesPolicy", wxChoice )->SetSelection( m_pTarget->GetOptionRelation( ortIncludeDirs ) );
      XRCCTRL( *this, "cmbLibDirsPolicy", wxChoice )->SetSelection( m_pTarget->GetOptionRelation( ortLibDirs ) );
      XRCCTRL( *this, "txtMakeCmd_Build", wxTextCtrl )->SetValue( m_pTarget->GetMakeCommandFor( mcBuild ) );
      XRCCTRL( *this, "txtMakeCmd_Compile", wxTextCtrl )->SetValue( m_pTarget->GetMakeCommandFor( mcCompileFile ) );
      XRCCTRL( *this, "txtMakeCmd_Clean", wxTextCtrl )->SetValue( m_pTarget->GetMakeCommandFor( mcClean ) );
      XRCCTRL( *this, "txtMakeCmd_DistClean", wxTextCtrl )->SetValue( m_pTarget->GetMakeCommandFor( mcDistClean ) );
      XRCCTRL( *this, "txtMakeCmd_AskRebuildNeeded", wxTextCtrl )->SetValue( m_pTarget->GetMakeCommandFor( mcAskRebuildNeeded ) );
      XRCCTRL( *this, "txtMakeCmd_SilentBuild", wxTextCtrl )->SetValue( m_pTarget->GetMakeCommandFor( mcSilentBuild ) );
    }
  }
  TextToOptions();
  DoFillOptions();
  ArrayString2ListBox( IncludeDirs, XRCCTRL( *this, "lstIncludeDirs", wxListBox ) );
  ArrayString2ListBox( LibDirs, XRCCTRL( *this, "lstLibDirs", wxListBox ) );
  ArrayString2TextCtrl( m_CompilerOptions, XRCCTRL( *this, "txtCompilerOptions", wxTextCtrl ) );
  ArrayString2TextCtrl( m_LinkerOptions, XRCCTRL( *this, "txtLinkerOptions", wxTextCtrl ) );
  if( m_pProject ) {
    ArrayString2TextCtrl( CommandsBeforeBuild, XRCCTRL( *this, "txtCmdBefore", wxTextCtrl ) );
    ArrayString2TextCtrl( CommandsAfterBuild, XRCCTRL( *this, "txtCmdAfter", wxTextCtrl ) );
    XRCCTRL( *this, "chkAlwaysRunPost", wxCheckBox )->SetValue( AlwaysUsePost );
  }
}

void CompilerOptionsDlg::OptionsToText() {
  wxArrayString array;
  DoGetCompileOptions( array, XRCCTRL( *this, "txtCompilerDefines", wxTextCtrl ) );
  int compilerIdx = XRCCTRL( *this, "cmbCompiler", wxChoice )->GetSelection();
  const Compiler* compiler = CompilerFactory::GetCompiler( compilerIdx );
  for( unsigned int i = 0; i < array.GetCount(); ++i ) {
    if( !array[i].IsEmpty() ) {
      if( array[i].StartsWith( compiler ? compiler->GetSwitches().genericSwitch : _T( "-" ) ) ) {
        if( m_CompilerOptions.Index( array[i] ) == wxNOT_FOUND ) {
          m_CompilerOptions.Add( array[i] );
        }
      } else {
        if( compiler && m_CompilerOptions.Index( compiler->GetSwitches().defines + array[i] ) == wxNOT_FOUND ) {
          m_CompilerOptions.Add( compiler->GetSwitches().defines + array[i] );
        }
      }
    }
  }
  wxArrayString compilerOpConflicts;
  wxArrayString linkerOpConflicts;
  for( size_t i = 0; i < m_Options.GetCount(); ++i ) {
    CompOption* copt = m_Options.GetOption( i );
    if( copt->enabled ) {
      if( !copt->option.Trim().IsEmpty() ) {
        m_CompilerOptions.Insert( copt->option, 0 );
      }
      if( !copt->additionalLibs.Trim().IsEmpty() ) {
        if( m_LinkerOptions.Index( copt->additionalLibs ) == wxNOT_FOUND ) {
          m_LinkerOptions.Insert( copt->additionalLibs, 0 );
        }
      }
    } else {
      if( m_CompilerOptions.Index( copt->option ) != wxNOT_FOUND ) {
        compilerOpConflicts.Add( copt->option );
      }
      if( m_LinkerOptions.Index( copt->additionalLibs ) != wxNOT_FOUND ) {
        linkerOpConflicts.Add( copt->additionalLibs );
      }
    }
  }
  if( !compilerOpConflicts.IsEmpty() || !linkerOpConflicts.IsEmpty() ) {
    wxString msg = _T( "编译器标志\n  " ) + GetStringFromArray( compilerOpConflicts, _T( "\n  " ) )
                   + GetStringFromArray( linkerOpConflicts, _T( "\n  " ) );
    msg.RemoveLast( 2 );
    msg += _T( "在“其他选项”中声明，但在“编译器标志”中未选中. 是否要启用这些标志?" );
    if( msging( msg, _T( "启用编译器标志?" ), wxYES_NO ) == wxID_YES ) {
      for( size_t i = 0; i < compilerOpConflicts.GetCount(); ++i ) {
        m_CompilerOptions.Remove( compilerOpConflicts[i] );
      }
      for( size_t i = 0; i < linkerOpConflicts.GetCount(); ++i ) {
        m_LinkerOptions.Remove( linkerOpConflicts[i] );
      }
    }
  }
}

void CompilerOptionsDlg::DoSaveOptions() {
  wxArrayString IncludeDirs;
  wxArrayString LibDirs;
  ListBox2ArrayString( IncludeDirs, XRCCTRL( *this, "lstIncludeDirs", wxListBox ) );
  ListBox2ArrayString( LibDirs, XRCCTRL( *this, "lstLibDirs", wxListBox ) );
  DoGetCompileOptions( m_CompilerOptions, XRCCTRL( *this, "txtCompilerOptions", wxTextCtrl ) );
  DoGetCompileOptions( m_ResourceCompilerOptions, XRCCTRL( *this, "txtResourceCompilerOptions", wxTextCtrl ) );
  DoGetCompileOptions( m_LinkerOptions, XRCCTRL( *this, "txtLinkerOptions", wxTextCtrl ) );
  OptionsToText();
  if( !m_pProject && !m_pTarget ) {
    Compiler* compiler = CompilerFactory::GetCompiler( m_CurrentCompilerIdx );
    if( compiler ) {
      compiler->SetIncludeDirs( IncludeDirs );
      compiler->SetLibDirs( LibDirs );
      compiler->SetCompilerOptions( m_CompilerOptions );
      compiler->SetLinkerOptions( m_LinkerOptions );
      compiler->SetLinkLibs( m_LinkLibs );
      wxChoice* cmb = XRCCTRL( *this, "cmbLogging", wxChoice );
      if( cmb ) {
        CompilerSwitches switches = compiler->GetSwitches();
        switches.logging = ( CompilerLoggingType )cmb->GetSelection();
        compiler->SetSwitches( switches );
      }
    }
  } else {
    wxArrayString CommandsBeforeBuild;
    wxArrayString CommandsAfterBuild;
    bool AlwaysUsePost = false;
    if( m_pProject ) {
      AlwaysUsePost = XRCCTRL( *this, "chkAlwaysRunPost", wxCheckBox )->GetValue();
      DoGetCompileOptions( CommandsBeforeBuild, XRCCTRL( *this, "txtCmdBefore", wxTextCtrl ) );
      DoGetCompileOptions( CommandsAfterBuild, XRCCTRL( *this, "txtCmdAfter", wxTextCtrl ) );
    }
    if( !m_pTarget ) {
      m_pProject->SetIncludeDirs( IncludeDirs );
      m_pProject->SetLibDirs( LibDirs );
      m_pProject->SetCompilerOptions( m_CompilerOptions );
      m_pProject->SetLinkerOptions( m_LinkerOptions );
      m_pProject->SetLinkLibs( m_LinkLibs );
      m_pProject->SetCommandsBeforeBuild( CommandsBeforeBuild );
      m_pProject->SetCommandsAfterBuild( CommandsAfterBuild );
      m_pProject->SetAlwaysRunPostBuildSteps( AlwaysUsePost );
      m_pProject->SetMakeCommandFor( mcBuild, XRCCTRL( *this, "txtMakeCmd_Build", wxTextCtrl )->GetValue() );
      m_pProject->SetMakeCommandFor( mcCompileFile, XRCCTRL( *this, "txtMakeCmd_Compile", wxTextCtrl )->GetValue() );
      m_pProject->SetMakeCommandFor( mcClean, XRCCTRL( *this, "txtMakeCmd_Clean", wxTextCtrl )->GetValue() );
      m_pProject->SetMakeCommandFor( mcDistClean, XRCCTRL( *this, "txtMakeCmd_DistClean", wxTextCtrl )->GetValue() );
      m_pProject->SetMakeCommandFor( mcAskRebuildNeeded, XRCCTRL( *this, "txtMakeCmd_AskRebuildNeeded", wxTextCtrl )->GetValue() );
      m_pProject->SetMakeCommandFor( mcSilentBuild, XRCCTRL( *this, "txtMakeCmd_Build", wxTextCtrl )->GetValue() + _T( " > $(CMD_NULL)" ) );
    } else {
      m_pTarget->SetIncludeDirs( IncludeDirs );
      m_pTarget->SetLibDirs( LibDirs );
      m_pTarget->SetCompilerOptions( m_CompilerOptions );
      m_pTarget->SetLinkerOptions( m_LinkerOptions );
      m_pTarget->SetLinkLibs( m_LinkLibs );
      m_pTarget->SetOptionRelation( ortCompilerOptions, OptionsRelation( XRCCTRL( *this, "cmbCompilerPolicy", wxChoice )->GetSelection() ) );
      m_pTarget->SetOptionRelation( ortLinkerOptions, OptionsRelation( XRCCTRL( *this, "cmbLinkerPolicy", wxChoice )->GetSelection() ) );
      m_pTarget->SetOptionRelation( ortIncludeDirs, OptionsRelation( XRCCTRL( *this, "cmbIncludesPolicy", wxChoice )->GetSelection() ) );
      m_pTarget->SetOptionRelation( ortLibDirs, OptionsRelation( XRCCTRL( *this, "cmbLibDirsPolicy", wxChoice )->GetSelection() ) );
      m_pTarget->SetCommandsBeforeBuild( CommandsBeforeBuild );
      m_pTarget->SetCommandsAfterBuild( CommandsAfterBuild );
      m_pTarget->SetAlwaysRunPostBuildSteps( AlwaysUsePost );
      m_pTarget->SetMakeCommandFor( mcBuild, XRCCTRL( *this, "txtMakeCmd_Build", wxTextCtrl )->GetValue() );
      m_pTarget->SetMakeCommandFor( mcCompileFile, XRCCTRL( *this, "txtMakeCmd_Compile", wxTextCtrl )->GetValue() );
      m_pTarget->SetMakeCommandFor( mcClean, XRCCTRL( *this, "txtMakeCmd_Clean", wxTextCtrl )->GetValue() );
      m_pTarget->SetMakeCommandFor( mcDistClean, XRCCTRL( *this, "txtMakeCmd_DistClean", wxTextCtrl )->GetValue() );
      m_pTarget->SetMakeCommandFor( mcAskRebuildNeeded, XRCCTRL( *this, "txtMakeCmd_AskRebuildNeeded", wxTextCtrl )->GetValue() );
      m_pTarget->SetMakeCommandFor( mcSilentBuild, XRCCTRL( *this, "txtMakeCmd_Build", wxTextCtrl )->GetValue() + _T( " > $(CMD_NULL)" ) );
    }
  }
}

void CompilerOptionsDlg::DoSaveCompilerPrograms() {
  Compiler* compiler = CompilerFactory::GetCompiler( m_CurrentCompilerIdx );
  if( m_pProject || !compiler ) {
    return;
  }
  CompilerPrograms progs;
  wxString masterPath = XRCCTRL( *this, "txtMasterPath", wxTextCtrl )->GetValue();
  progs.C = ( XRCCTRL( *this, "txtCcompiler", wxTextCtrl )->GetValue() ).Trim();
  progs.CPP = ( XRCCTRL( *this, "txtCPPcompiler", wxTextCtrl )->GetValue() ).Trim();
  progs.LD = ( XRCCTRL( *this, "txtLinker", wxTextCtrl )->GetValue() ).Trim();
  progs.LIB = ( XRCCTRL( *this, "txtLibLinker", wxTextCtrl )->GetValue() ).Trim();
  progs.MAKE = ( XRCCTRL( *this, "txtMake", wxTextCtrl )->GetValue() ).Trim();
  wxChoice *cmbDebugger = XRCCTRL( *this, "cmbDebugger", wxChoice );
  if( cmbDebugger ) {
    int index = cmbDebugger->GetSelection();
    const DebuggerClientData* data = static_cast<const DebuggerClientData*>( cmbDebugger->GetClientObject( index ) );
    progs.DBGconfig = data->string;
  }
  compiler->SetPrograms( progs );
  compiler->SetMasterPath( masterPath );
  wxListBox* control = XRCCTRL( *this, "lstExtraPaths", wxListBox );
  if( control ) {
    wxArrayString extraPaths;
    ListBox2ArrayString( extraPaths, control );
    compiler->SetExtraPaths( extraPaths );
  }
}

void CompilerOptionsDlg::DoSaveVars() {
  CompileOptionsBase* pBase = GetVarsOwner();
  if( pBase ) {
    for( unsigned int idxAction = 0; idxAction < m_CustomVarActions.size(); ++idxAction ) {
      CustomVarAction Action = m_CustomVarActions[idxAction];
      switch( Action.m_Action ) {
        case CVA_Add:
          pBase->SetVar( Action.m_Key, Action.m_KeyValue );
          break;
        case CVA_Edit: {
          wxString NewKey = Action.m_KeyValue.BeforeFirst( _T( '=' ) ).Trim( true ).Trim( false );
          wxString NewValue = Action.m_KeyValue.AfterFirst( _T( '=' ) ).Trim( true ).Trim( false );
          if( Action.m_Key != NewKey ) {
            pBase->UnsetVar( Action.m_Key );
          }
          pBase->SetVar( NewKey, NewValue );
          break;
        }
        case CVA_Remove:
          pBase->UnsetVar( Action.m_Key );
          break;
        default:
          break;
      }
    }
    m_CustomVarActions.clear();
  }
}

void CompilerOptionsDlg::DoSaveCompilerDefinition() {
  wxXmlNode* root = new wxXmlNode( wxXML_ELEMENT_NODE, _T( "m_Code_compiler_options" ) );
  Compiler* compiler = CompilerFactory::GetCompiler( m_CurrentCompilerIdx );
  const wxString name = _T( "name" );
  const wxString value = _T( "value" );
  wxXmlNode* node = new wxXmlNode( root, wxXML_ELEMENT_NODE, _T( "Program" ) );
  node->AddAttribute( name, _T( "C" ) );
  node->AddAttribute( value, compiler->GetPrograms().C );
  node->SetNext( new wxXmlNode( wxXML_ELEMENT_NODE, _T( "Program" ) ) );
  node = node->GetNext();
  node->AddAttribute( name, _T( "CPP" ) );
  node->AddAttribute( value, compiler->GetPrograms().CPP );
  node->SetNext( new wxXmlNode( wxXML_ELEMENT_NODE, _T( "Program" ) ) );
  node = node->GetNext();
  node->AddAttribute( name, _T( "LD" ) );
  node->AddAttribute( value, compiler->GetPrograms().LD );
  node->SetNext( new wxXmlNode( wxXML_ELEMENT_NODE, _T( "Program" ) ) );
  node = node->GetNext();
  node->AddAttribute( name, _T( "DBGconfig" ) );
  node->AddAttribute( value, compiler->GetPrograms().DBGconfig );
  node->SetNext( new wxXmlNode( wxXML_ELEMENT_NODE, _T( "Program" ) ) );
  node = node->GetNext();
  node->AddAttribute( name, _T( "LIB" ) );
  node->AddAttribute( value, compiler->GetPrograms().LIB );
  node->SetNext( new wxXmlNode( wxXML_ELEMENT_NODE, _T( "Program" ) ) );
  node = node->GetNext();
  node->AddAttribute( name, _T( "MAKE" ) );
  node->AddAttribute( value, compiler->GetPrograms().MAKE );
  node->SetNext( new wxXmlNode( wxXML_ELEMENT_NODE, _T( "Switch" ) ) );
  node = node->GetNext();
  node->AddAttribute( name, _T( "includeDirs" ) );
  node->AddAttribute( value, compiler->GetSwitches().includeDirs );
  node->SetNext( new wxXmlNode( wxXML_ELEMENT_NODE, _T( "Switch" ) ) );
  node = node->GetNext();
  node->AddAttribute( name, _T( "libDirs" ) );
  node->AddAttribute( value, compiler->GetSwitches().libDirs );
  node->SetNext( new wxXmlNode( wxXML_ELEMENT_NODE, _T( "Switch" ) ) );
  node = node->GetNext();
  node->AddAttribute( name, _T( "linkLibs" ) );
  node->AddAttribute( value, compiler->GetSwitches().linkLibs );
  node->SetNext( new wxXmlNode( wxXML_ELEMENT_NODE, _T( "Switch" ) ) );
  node = node->GetNext();
  node->AddAttribute( name, _T( "defines" ) );
  node->AddAttribute( value, compiler->GetSwitches().defines );
  node->SetNext( new wxXmlNode( wxXML_ELEMENT_NODE, _T( "Switch" ) ) );
  node = node->GetNext();
  node->AddAttribute( name, _T( "genericSwitch" ) );
  node->AddAttribute( value, compiler->GetSwitches().genericSwitch );
  node->SetNext( new wxXmlNode( wxXML_ELEMENT_NODE, _T( "Switch" ) ) );
  node = node->GetNext();
  node->AddAttribute( name, _T( "objectExtension" ) );
  node->AddAttribute( value, compiler->GetSwitches().objectExtension );
  node->SetNext( new wxXmlNode( wxXML_ELEMENT_NODE, _T( "Switch" ) ) );
  node = node->GetNext();
  node->AddAttribute( name, _T( "forceFwdSlashes" ) );
  node->AddAttribute( value, ( compiler->GetSwitches().forceFwdSlashes ? _T( "true" ) : _T( "false" ) ) );
  node->SetNext( new wxXmlNode( wxXML_ELEMENT_NODE, _T( "Switch" ) ) );
  node = node->GetNext();
  node->AddAttribute( name, _T( "forceLinkerUseQuotes" ) );
  node->AddAttribute( value, ( compiler->GetSwitches().forceLinkerUseQuotes ? _T( "true" ) : _T( "false" ) ) );
  node->SetNext( new wxXmlNode( wxXML_ELEMENT_NODE, _T( "Switch" ) ) );
  node = node->GetNext();
  node->AddAttribute( name, _T( "forceCompilerUseQuotes" ) );
  node->AddAttribute( value, ( compiler->GetSwitches().forceCompilerUseQuotes ? _T( "true" ) : _T( "false" ) ) );
  node->SetNext( new wxXmlNode( wxXML_ELEMENT_NODE, _T( "Switch" ) ) );
  node = node->GetNext();
  node->AddAttribute( name, _T( "needDependencies" ) );
  node->AddAttribute( value, ( compiler->GetSwitches().needDependencies ? _T( "true" ) : _T( "false" ) ) );
  node->SetNext( new wxXmlNode( wxXML_ELEMENT_NODE, _T( "Switch" ) ) );
  node = node->GetNext();
  node->AddAttribute( name, _T( "logging" ) );
  if( compiler->GetSwitches().logging == CompilerSwitches::defaultLogging ) {
    node->AddAttribute( value, _T( "default" ) );
  } else if( compiler->GetSwitches().logging == clogFull ) {
    node->AddAttribute( value, _T( "full" ) );
  } else if( compiler->GetSwitches().logging == clogSimple ) {
    node->AddAttribute( value, _T( "simple" ) );
  } else if( compiler->GetSwitches().logging == clogNone ) {
    node->AddAttribute( value, _T( "none" ) );
  }
  node->SetNext( new wxXmlNode( wxXML_ELEMENT_NODE, _T( "Switch" ) ) );
  node = node->GetNext();
  node->AddAttribute( name, _T( "libPrefix" ) );
  node->AddAttribute( value, compiler->GetSwitches().libPrefix );
  node->SetNext( new wxXmlNode( wxXML_ELEMENT_NODE, _T( "Switch" ) ) );
  node = node->GetNext();
  node->AddAttribute( name, _T( "libExtension" ) );
  node->AddAttribute( value, compiler->GetSwitches().libExtension );
  node->SetNext( new wxXmlNode( wxXML_ELEMENT_NODE, _T( "Switch" ) ) );
  node = node->GetNext();
  node->AddAttribute( name, _T( "linkerNeedsLibPrefix" ) );
  node->AddAttribute( value, ( compiler->GetSwitches().linkerNeedsLibPrefix ? _T( "true" ) : _T( "false" ) ) );
  node->SetNext( new wxXmlNode( wxXML_ELEMENT_NODE, _T( "Switch" ) ) );
  node = node->GetNext();
  node->AddAttribute( name, _T( "linkerNeedsLibExtension" ) );
  node->AddAttribute( value, ( compiler->GetSwitches().linkerNeedsLibExtension ? _T( "true" ) : _T( "false" ) ) );
  node->SetNext( new wxXmlNode( wxXML_ELEMENT_NODE, _T( "Switch" ) ) );
  node = node->GetNext();
  node->AddAttribute( name, _T( "linkerNeedsPathResolved" ) );
  node->AddAttribute( value, ( compiler->GetSwitches().linkerNeedsPathResolved ? _T( "true" ) : _T( "false" ) ) );
  node->SetNext( new wxXmlNode( wxXML_ELEMENT_NODE, _T( "Switch" ) ) );
  node = node->GetNext();
  node->AddAttribute( name, _T( "supportsPCH" ) );
  node->AddAttribute( value, ( compiler->GetSwitches().supportsPCH ? _T( "true" ) : _T( "false" ) ) );
  node->SetNext( new wxXmlNode( wxXML_ELEMENT_NODE, _T( "Switch" ) ) );
  node = node->GetNext();
  node->AddAttribute( name, _T( "PCHExtension" ) );
  node->AddAttribute( value, compiler->GetSwitches().PCHExtension );
  node->SetNext( new wxXmlNode( wxXML_ELEMENT_NODE, _T( "Switch" ) ) );
  node = node->GetNext();
  node->AddAttribute( name, _T( "UseFlatObjects" ) );
  node->AddAttribute( value, ( compiler->GetSwitches().UseFlatObjects ? _T( "true" ) : _T( "false" ) ) );
  node->SetNext( new wxXmlNode( wxXML_ELEMENT_NODE, _T( "Switch" ) ) );
  node = node->GetNext();
  node->AddAttribute( name, _T( "UseFullSourcePaths" ) );
  node->AddAttribute( value, ( compiler->GetSwitches().UseFullSourcePaths ? _T( "true" ) : _T( "false" ) ) );
  node->SetNext( new wxXmlNode( wxXML_ELEMENT_NODE, _T( "Switch" ) ) );
  node = node->GetNext();
  node->AddAttribute( name, _T( "includeDirSeparator" ) );
  node->AddAttribute( value, compiler->GetSwitches().includeDirSeparator );
  node->SetNext( new wxXmlNode( wxXML_ELEMENT_NODE, _T( "Switch" ) ) );
  node = node->GetNext();
  node->AddAttribute( name, _T( "libDirSeparator" ) );
  node->AddAttribute( value, compiler->GetSwitches().libDirSeparator );
  node->SetNext( new wxXmlNode( wxXML_ELEMENT_NODE, _T( "Switch" ) ) );
  node = node->GetNext();
  node->AddAttribute( name, _T( "objectSeparator" ) );
  node->AddAttribute( value, compiler->GetSwitches().objectSeparator );
  node->SetNext( new wxXmlNode( wxXML_ELEMENT_NODE, _T( "Switch" ) ) );
  node = node->GetNext();
  node->AddAttribute( name, _T( "statusSuccess" ) );
  node->AddAttribute( value, F( _T( "%d" ), compiler->GetSwitches().statusSuccess ) );
  node->SetNext( new wxXmlNode( wxXML_ELEMENT_NODE, _T( "Switch" ) ) );
  for( size_t i = 0; i < m_Options.GetCount(); ++i ) {
    CompOption* opt = m_Options.GetOption( i );
    node->SetNext( new wxXmlNode( wxXML_ELEMENT_NODE, _T( "Option" ) ) );
    node = node->GetNext();
    wxString oName;
    opt->name.EndsWith( _T( "[" ) + opt->option + _T( "]" ), &oName );
    if( oName == wxEmptyString ) {
      opt->name.EndsWith( _T( "[" ) + opt->additionalLibs + _T( "]" ), &oName );
    }
    if( oName == wxEmptyString ) {
      oName = opt->name;
    }
    node->AddAttribute( name, oName.Trim() );
    if( opt->option != wxEmptyString ) {
      node->AddAttribute( _T( "option" ), opt->option );
    }
    if( opt->category != _T( "General" ) ) {
      node->AddAttribute( _T( "category" ), opt->category );
    }
    if( opt->additionalLibs != wxEmptyString ) {
      node->AddAttribute( _T( "additionalLibs" ), opt->additionalLibs );
    }
    if( opt->checkAgainst != wxEmptyString ) {
      node->AddAttribute( _T( "checkAgainst" ), opt->checkAgainst );
      node->AddAttribute( _T( "checkMessage" ), opt->checkMessage );
    }
    if( opt->supersedes != wxEmptyString ) {
      node->AddAttribute( _T( "supersedes" ), opt->supersedes );
    }
    if( opt->exclusive ) {
      node->AddAttribute( _T( "exclusive" ), _T( "true" ) );
    }
  }
  for( int i = 0; i < ctCount; ++i ) {
    const CompilerToolsVector& vec = compiler->GetCommandToolsVector( ( CommandType )i );
    wxString op;
    if( i == ctCompileObjectCmd ) {
      op = _T( "CompileObject" );
    } else if( i == ctGenDependenciesCmd ) {
      op = _T( "GenDependencies" );
    } else if( i == ctLinkExeCmd ) {
      op = _T( "LinkExe" );
    } else if( i == ctLinkConsoleExeCmd ) {
      op = _T( "LinkConsoleExe" );
    } else if( i == ctLinkDynamicCmd ) {
      op = _T( "LinkDynamic" );
    } else if( i == ctLinkStaticCmd ) {
      op = _T( "LinkStatic" );
    } else if( i == ctLinkNativeCmd ) {
      op = _T( "LinkNative" );
    }
    for( size_t j = 0; j < vec.size(); ++j ) {
      node->SetNext( new wxXmlNode( wxXML_ELEMENT_NODE, _T( "Command" ) ) );
      node = node->GetNext();
      node->AddAttribute( name, op );
      node->AddAttribute( value, vec[j].command );
      if( !vec[j].extensions.IsEmpty() ) {
        node->AddAttribute( _T( "ext" ), GetStringFromArray( vec[j].extensions, DEFAULT_ARRAY_SEP, false ) );
      }
      if( !vec[j].generatedFiles.IsEmpty() ) {
        node->AddAttribute( _T( "gen" ), GetStringFromArray( vec[j].generatedFiles, DEFAULT_ARRAY_SEP, false ) );
      }
    }
  }
  const RegExArray& regexes = compiler->GetRegExArray();
  for( size_t i = 0; i < regexes.size(); ++i ) {
    node->SetNext( new wxXmlNode( wxXML_ELEMENT_NODE, _T( "RegEx" ) ) );
    node = node->GetNext();
    node->AddAttribute( name, regexes[i].desc );
    wxString tp;
    if( regexes[i].lt == cltNormal ) {
      tp = _T( "normal" );
    } else if( regexes[i].lt == cltWarning ) {
      tp = _T( "warning" );
    } else if( regexes[i].lt == cltError ) {
      tp = _T( "error" );
    } else if( regexes[i].lt == cltInfo ) {
      tp = _T( "info" );
    }
    node->AddAttribute( _T( "type" ), tp );
    tp = F( _T( "%d;%d;%d" ), regexes[i].msg[0], regexes[i].msg[1], regexes[i].msg[2] );
    tp.Replace( _T( ";0" ), wxEmptyString );
    node->AddAttribute( _T( "msg" ), tp );
    if( regexes[i].filename != 0 ) {
      node->AddAttribute( _T( "file" ), F( _T( "%d" ), regexes[i].filename ) );
    }
    if( regexes[i].line != 0 ) {
      node->AddAttribute( _T( "line" ), F( _T( "%d" ), regexes[i].line ) );
    }
    tp = regexes[i].GetRegExString();
    tp.Replace( _T( "\t" ), _T( "\\t" ) );
    node->AddChild( new wxXmlNode( wxXML_CDATA_SECTION_NODE, wxEmptyString, tp ) );
  }
  if( !compiler->GetCOnlyFlags().IsEmpty() ) {
    node->SetNext( new wxXmlNode( wxXML_ELEMENT_NODE, _T( "Sort" ) ) );
    node = node->GetNext();
    node->AddAttribute( _T( "CFlags" ), compiler->GetCOnlyFlags() );
  }
  if( !compiler->GetCPPOnlyFlags().IsEmpty() ) {
    node->SetNext( new wxXmlNode( wxXML_ELEMENT_NODE, _T( "Sort" ) ) );
    node = node->GetNext();
    node->AddAttribute( _T( "CPPFlags" ), compiler->GetCPPOnlyFlags() );
  }
  wxXmlDocument doc;
  doc.SetRoot( root );
  if( !wxDirExists( ConfigManager::GetFolder( sdDataGlobal ) + _T( "/compilers" ) ) ) {
    wxMkdir( ConfigManager::GetFolder( sdDataGlobal ) + _T( "/compilers" ) );
  }
  doc.Save( ConfigManager::GetFolder( sdDataGlobal ) + _T( "/compilers/options_" ) + compiler->GetID() + _T( ".xml" ) );
  compiler->SetOptions( m_Options );
}


void CompilerOptionsDlg::OnDirty( M_Code_unused wxCommandEvent& event ) {
  m_bDirty = true;
}

void CompilerOptionsDlg::ProjectTargetCompilerAdjust() {
  if( m_pTarget ) {
    if( !m_NewProjectOrTargetCompilerId.IsEmpty() && m_pTarget->GetCompilerID() != m_NewProjectOrTargetCompilerId ) {
      m_pTarget->SetCompilerID( m_NewProjectOrTargetCompilerId );
      msging( _T( "更改了用于此目标的编译器.\n 建议完全重建此目标, 否则可能发生链接错误..." ), _T( "Notice" ),wxICON_EXCLAMATION );
    }
  } else if( m_pProject ) {
    if( !m_NewProjectOrTargetCompilerId.IsEmpty() && m_pProject->GetCompilerID() != m_NewProjectOrTargetCompilerId ) {
      m_pProject->SetCompilerID( m_NewProjectOrTargetCompilerId );
      UpdateCompilerForTargets( m_CurrentCompilerIdx );
      msging( _T( "更改了用于此项目的编译器.\n建议完全重建此项目, 否则可能发生链接错误..." ), _T( "Notice" ), wxICON_EXCLAMATION );
    }
  }
  m_NewProjectOrTargetCompilerId = wxEmptyString;
}

void CompilerOptionsDlg::OnTreeSelectionChange( wxTreeEvent& event ) {
  if( m_BuildingTree ) {
    return;
  }
  wxTreeCtrl* tc = XRCCTRL( *this, "tcScope", wxTreeCtrl );
  ScopeTreeData* data = ( ScopeTreeData* )tc->GetItemData( event.GetItem() );
  if( !data ) {
    return;
  }
  int compilerIdx = data->GetTarget() ? CompilerFactory::GetCompilerIndex( data->GetTarget()->GetCompilerID() ) :
                    ( data->GetProject() ? CompilerFactory::GetCompilerIndex( data->GetProject()->GetCompilerID() ) :
                      XRCCTRL( *this, "cmbCompiler", wxChoice )->GetSelection() );
  if( compilerIdx != -1 ) {
    wxNotebook* nb = XRCCTRL( *this, "nbMain", wxNotebook );
    XRCCTRL( *this, "cmbCompiler", wxChoice )->SetSelection( compilerIdx );
    m_pTarget = data->GetTarget();
    if( nb ) {
      const bool cmd = ( m_pTarget && m_pTarget->GetTargetType() == ttCommandsOnly );
      int pageOffset;
      if( !m_pProject->IsMakefileCustom() ) {
        nb->GetPage( 0 )->Enable( !cmd );
        nb->GetPage( 1 )->Enable( !cmd );
        nb->GetPage( 2 )->Enable( !cmd );
        pageOffset = 3;
      } else {
        pageOffset = 0;
      }
      nb->GetPage( pageOffset + 2 )->Enable( !cmd );
      if( cmd && nb->GetSelection() != pageOffset && nb->GetSelection() != pageOffset + 1 ) {
        nb->SetSelection( pageOffset );
      }
      nb->Enable();
    }
    m_CurrentCompilerIdx = compilerIdx;
    Compiler* compiler = CompilerFactory::GetCompiler( m_CurrentCompilerIdx );
    if( compiler ) {
      m_Options = compiler->GetOptions();
    }
    DoFillCompilerDependentSettings();
  } else {
    m_pTarget = data->GetTarget();
    wxString CompilerId = m_pTarget ? m_pTarget->GetCompilerID() : data->GetProject()->GetCompilerID();
    wxString msg;
    msg.Printf( _T( "找不到定义的编译器 (ID: %s)" ), CompilerId.wx_str() );
    Compiler* compiler = 0;
    if( m_pTarget ) {
      compiler = CompilerFactory::SelectCompilerUI( msg );
    }
    if( compiler ) {
      int NewCompilerIdx = CompilerFactory::GetCompilerIndex( compiler );
      XRCCTRL( *this, "cmbCompiler", wxChoice )->SetSelection( NewCompilerIdx );
      wxCommandEvent Dummy;
      OnCompilerChanged( Dummy );
    } else {
      if( wxNotebook* nb = XRCCTRL( *this, "nbMain", wxNotebook ) ) {
        nb->Disable();
      }
    }
  }
}

void CompilerOptionsDlg::OnTreeSelectionChanging( wxTreeEvent& event ) {
  if( m_BuildingTree ) {
    return;
  }
  wxTreeCtrl* tc = XRCCTRL( *this, "tcScope", wxTreeCtrl );
  ScopeTreeData* data = ( ScopeTreeData* )tc->GetItemData( event.GetOldItem() );
  if( data && ( m_bDirty || m_bFlagsDirty ) ) {
    InfoWindow::Tip( _T( "项目/目标 更改了设置以更新" ), _T( "提示" ), 2500 );
    DoSaveCompilerDependentSettings();
  }
}

void CompilerOptionsDlg::OnCompilerChanged( M_Code_unused wxCommandEvent& event ) {   // 编译器被改变触发
  if( m_bDirty || m_bFlagsDirty ) {
    InfoWindow::Tip( _T( "更改了设置以更新" ), _T( "提示" ), 2500 );
    DoSaveCompilerDependentSettings();
    //CheckCompilerOptions(true);
  }
  CompilerChanged();
  if( m_pProject ) {
    m_bDirty = true;
  }
}

void CompilerOptionsDlg::CompilerChanged() {
  m_CurrentCompilerIdx = XRCCTRL( *this, "cmbCompiler", wxChoice )->GetSelection();
  if( m_pProject ) {
    m_NewProjectOrTargetCompilerId = CompilerFactory::GetCompiler( m_CurrentCompilerIdx )->GetID();
  }
  Compiler* compiler = CompilerFactory::GetCompiler( m_CurrentCompilerIdx );
  if( compiler ) {
    m_Options = compiler->GetOptions();
  }
  DoFillCompilerDependentSettings();
}

void CompilerOptionsDlg::UpdateCompilerForTargets( int compilerIdx ) {
  int ret = msging( _T( "更改了项目的编译器 是否对项目的所有生成目标也使用相同的编译器?" ), _T( "询问" ), wxICON_QUESTION | wxYES_NO );
  if( ret == wxID_YES ) {
    for( int i = 0; i < m_pProject->GetBuildTargetsCount(); ++i ) {
      ProjectBuildTarget* target = m_pProject->GetBuildTarget( i );
      Compiler* compiler = CompilerFactory::GetCompiler( compilerIdx );
      if( compiler ) {
        target->SetCompilerID( compiler->GetID() );
      }
    }
  }
}

void CompilerOptionsDlg::AutoDetectCompiler() {
  Compiler* compiler = CompilerFactory::GetCompiler( m_CurrentCompilerIdx );
  if( !compiler ) {
    return;
  }
  wxString backup = XRCCTRL( *this, "txtMasterPath", wxTextCtrl )->GetValue();
  wxArrayString ExtraPathsBackup = compiler->GetExtraPaths();
  wxArrayString empty;
  compiler->SetExtraPaths( empty );
  switch( compiler->AutoDetectInstallationDir() ) {
    case adrDetected: {
      wxString msg;
      msg.Printf( _T( "自动检测的安装路径 \"%s\"\nin \"%s\"" ), compiler->GetName().wx_str(), compiler->GetMasterPath().wx_str() );
      InfoWindow::Tip( msg, _T( "提示" ), 2500 );
    }
    break;
    case adrGuessed: {
      wxString msg;
      msg.Printf( _T( "无法自动检测的安装路径 \"%s\" 是否要使用此编译器的默认安装目录?" ),
                  compiler->GetName().wx_str() );
      if( msging( msg, _T( "审查" ), wxICON_QUESTION | wxYES_NO ) == wxID_NO ) {
        compiler->SetMasterPath( backup );
        compiler->SetExtraPaths( ExtraPathsBackup );
      }
    }
    break;
    default:
      break;
  }
  XRCCTRL( *this, "txtMasterPath", wxTextCtrl )->SetValue( compiler->GetMasterPath() );
  XRCCTRL( *this, "lstExtraPaths", wxListBox )->Clear();
  const wxArrayString& extraPaths = CompilerFactory::GetCompiler( m_CurrentCompilerIdx )->GetExtraPaths();
  ArrayString2ListBox( extraPaths, XRCCTRL( *this, "lstExtraPaths", wxListBox ) );
  m_bDirty = true;
}

wxListBox* CompilerOptionsDlg::GetDirsListBox() {
  wxNotebook* nb = XRCCTRL( *this, "nbDirs", wxNotebook );
  if( !nb ) {
    return 0;
  }
  switch( nb->GetSelection() ) {
    case 0:
      return XRCCTRL( *this, "lstIncludeDirs", wxListBox );
    case 1:
      return XRCCTRL( *this, "lstLibDirs", wxListBox );
    default:
      break;
  }
  return 0;
}

CompileOptionsBase* CompilerOptionsDlg::GetVarsOwner() {
  return m_pTarget ? m_pTarget : ( m_pProject ? m_pProject
                                   : ( CompileOptionsBase* )( CompilerFactory::GetCompiler( m_CurrentCompilerIdx ) ) );
}

void CompilerOptionsDlg::OnCategoryChanged( M_Code_unused wxCommandEvent& event ) {
  DoFillOptions();
}

void CompilerOptionsDlg::OnOptionChanged( wxPropertyGridEvent& event ) {
  wxPGProperty* property = event.GetProperty();
  if( !property ) {
    return;
  }
  if( !property->IsKindOf( CLASSINFO( wxBoolProperty ) ) ) {
    return;
  }
  CompOption* option = m_Options.GetOptionByName( property->GetLabel() );
  wxVariant value = property->GetValue();
  if( value.IsNull() || !option ) {
    return;
  }
  option->enabled = value.GetBool();
  if( option->enabled ) {
    if( !option->checkAgainst.IsEmpty() ) {
      wxArrayString check = GetArrayFromString( option->checkAgainst, _T( " " ) );
      for( size_t i = 0; i < check.Count(); ++i ) {
        CompOption* against = m_Options.GetOptionByOption( check[i] );
        if( !against ) {
          against = m_Options.GetOptionByAdditionalLibs( check[i] );
        }
        if( against && against->enabled ) {
          wxString message = ( option->checkMessage.IsEmpty() ?
                               _T( "\"" ) + option->name + _T( "\" 与冲突 \"" ) + against->name + _T( "\"." ) :
                               option->checkMessage );
          InfoWindow::Tip( message, _T( "提示" ), 2500 );
          break;
        }
      }
    }
    if( option->supersedes != wxEmptyString ) {
      wxArrayString supersede = GetArrayFromString( option->supersedes, _T( " " ) );
      for( size_t i = 0; i < supersede.Count(); ++i ) {
        for( size_t j = 0; j < m_Options.GetCount(); ++j ) {
          if( option != m_Options.GetOption( j ) &&
              ( supersede[i] == m_Options.GetOption( j )->option ||
                supersede[i] == m_Options.GetOption( j )->additionalLibs ) ) {
            m_Options.GetOption( j )->enabled = false;
          }
        }
        for( wxPropertyGridIterator it = m_FlagsPG->GetIterator(); !it.AtEnd(); ++it ) {
          wxPGProperty* p = *it;
          if( p->IsCategory() || p == property ) {
            continue;
          }
          if( p->GetLabel().EndsWith( _T( "[" ) + supersede[i] + _T( "]" ) ) ) {
            m_FlagsPG->SetPropertyValue( p, false );
          }
        }
      }
    }
    if( option->exclusive ) {
      for( size_t i = 0; i < m_Options.GetCount(); ++i ) {
        if( option != m_Options.GetOption( i ) &&
            option->category == m_Options.GetOption( i )->category ) {
          m_Options.GetOption( i )->enabled = false;
        }
      }
      for( wxPropertyGridIterator it = m_FlagsPG->GetIterator(); !it.AtEnd(); ++it ) {
        wxPGProperty* p = *it;
        if( p->IsCategory() || p == property ) {
          continue;
        }
        CompOption* opt = m_Options.GetOptionByName( p->GetLabel() );
        if( option != opt && option->category == opt->category ) {
          m_FlagsPG->SetPropertyValue( p, false );
        }
      }
    }
  }
  m_bDirty = true;
}

void CompilerOptionsDlg::OnAddDirClick( M_Code_unused wxCommandEvent& event ) {
  EditPathDlg dlg( this, m_pProject ? m_pProject->GetBasePath() : _T( "" ),
                   m_pProject ? m_pProject->GetBasePath() : _T( "" ), _T( "添加目录" ) );
  PlaceWindow( &dlg );
  if( dlg.ShowModal() == wxID_OK ) {
    wxString path = dlg.GetPath();
    wxListBox* control = GetDirsListBox();
    if( control ) {
      control->Append( path );
      m_bDirty = true;
    }
  }
}

void CompilerOptionsDlg::OnEditDirClick( M_Code_unused wxCommandEvent& event ) {
  wxListBox* control = GetDirsListBox();
  wxArrayInt selections;
  if( !control || control->GetSelections( selections ) < 1 ) {
    return;
  }
  if( selections.GetCount() > 1 ) {
    InfoWindow::Tip( _T( "请只选择一个要编辑的目录" ), _T( "错误" ), 3500 );
    return;
  }
  EditPathDlg dlg( this, control->GetString( selections[0] ), m_pProject ? m_pProject->GetBasePath() : _T( "" ), _T( "编辑目录" ) );
  PlaceWindow( &dlg );
  if( dlg.ShowModal() == wxID_OK ) {
    wxString path = dlg.GetPath();
    control->SetString( selections[0], path );
    m_bDirty = true;
  }
}

void CompilerOptionsDlg::OnRemoveDirClick( M_Code_unused wxCommandEvent& event ) {
  wxListBox* control = GetDirsListBox();
  wxArrayInt selections;
  if( !control || control->GetSelections( selections ) < 1 ) {
    return;
  }
  std::sort( selections.begin(), selections.end() );
  for( unsigned int i = selections.GetCount(); i > 0; --i ) {
    control->Delete( selections[i - 1] );
  }
  m_bDirty = true;
}

void CompilerOptionsDlg::OnClearDirClick( M_Code_unused wxCommandEvent& event ) {
  wxListBox* control = GetDirsListBox();
  if( !control || control->GetCount() == 0 ) {
    return;
  }
  control->Clear();
  m_bDirty = true;
}

void CompilerOptionsDlg::OnCopyDirsClick( M_Code_unused wxCommandEvent& event ) {
  if( !m_pProject ) {
    return;
  }
  wxListBox* control = GetDirsListBox();
  wxArrayInt selections;
  if( !control || control->GetSelections( selections ) < 1 ) {
    return;
  }
  wxArrayString choices;
  choices.Add( m_pProject->GetTitle() );
  for( int i = 0; i < m_pProject->GetBuildTargetsCount(); ++i ) {
    ProjectBuildTarget* bt = m_pProject->GetBuildTarget( i );
    choices.Add( bt->GetTitle() );
  }
  const wxArrayInt &sel = cbGetMultiChoiceDialog( _T( "请选择要将这些目录复制到哪个目标:" ), _T( "复制目录" ), choices, this );
  if( sel.empty() ) {
    return;
  }
  wxNotebook* nb = XRCCTRL( *this, "nbDirs", wxNotebook );
  int notebookPage = nb->GetSelection();
  for( wxArrayInt::const_iterator itr = sel.begin(); itr != sel.end(); ++itr ) {
    CompileOptionsBase* base;
    if( ( *itr ) == 0 ) {
      base = m_pProject;
    } else{ base = m_pProject->GetBuildTarget( ( *itr ) - 1 ); }
    if( !base ) {
      Manager::Get()->GetLogManager()->LogWarning( _T( "未能在中获取生成目标 CompilerOptionsDlg::OnCopyLibsClick" ) );
      continue;
    }
    for( size_t i = 0; i < selections.GetCount(); ++i ) {
      switch( notebookPage ) {
        case 0:
          base->AddIncludeDir( control->GetString( selections[i] ) );
          break;
        case 1:
          base->AddLibDir( control->GetString( selections[i] ) );
          break;
        default:
          break;
      }
    }
  }
}

static void QuoteString( wxString &value, const wxString &caption ) {
  if( NeedQuotes( value ) ) {
    if( msging( _T( "该值包含空格或奇怪字符。你想引用一下吗?" ), _T( "变量引号字符串" ), wxYES_NO ) == wxID_YES ) {
      ::QuoteStringIfNeeded( value );
    }
  }
}

void CompilerOptionsDlg::OnAddVarClick( M_Code_unused wxCommandEvent& event ) {
  wxString key;
  wxString value;
  EditPairDlg dlg( this, key, value, _T( "添加新变量" ), EditPairDlg::bmBrowseForDirectory );
  PlaceWindow( &dlg );
  if( dlg.ShowModal() == wxID_OK ) {
    key.Trim( true ).Trim( false );
    value.Trim( true ).Trim( false );
    QuoteString( value, _T( "添加变量引号字符串" ) );
    CustomVarAction Action = {CVA_Add, key, value};
    m_CustomVarActions.push_back( Action );
    XRCCTRL( *this, "lstVars", wxListBox )->Append( key + _T( " = " ) + value, new VariableListClientData( key, value ) );
    m_bDirty = true;
  }
}

void CompilerOptionsDlg::OnEditVarClick( M_Code_unused wxCommandEvent& event ) {
  wxListBox *list = XRCCTRL( *this, "lstVars", wxListBox );
  int sel = list->GetSelection();
  if( sel == -1 ) {
    return;
  }
  VariableListClientData *data = static_cast<VariableListClientData*>( list->GetClientObject( sel ) );
  wxString key = data->key;
  wxString value = data->value;
  EditPairDlg dlg( this, key, value, _T( "编辑变量" ), EditPairDlg::bmBrowseForDirectory );
  PlaceWindow( &dlg );
  if( dlg.ShowModal() == wxID_OK ) {
    key.Trim( true ).Trim( false );
    value.Trim( true ).Trim( false );
    QuoteString( value, _T( "编辑变量引号字符串" ) );
    if( value != data->value  ||  key != data->key ) {
      CustomVarAction Action = {CVA_Edit, data->key, key + _T( " = " ) + value};
      m_CustomVarActions.push_back( Action );
      list->SetString( sel, key + _T( " = " ) + value );
      data->key = key;
      data->value = value;
      m_bDirty = true;
    }
  }
}

void CompilerOptionsDlg::OnRemoveVarClick( M_Code_unused wxCommandEvent& event ) {
  wxListBox *list = XRCCTRL( *this, "lstVars", wxListBox );
  int sel = list->GetSelection();
  if( sel == -1 ) {
    return;
  }
  const wxString &key = static_cast<VariableListClientData*>( list->GetClientObject( sel ) )->key;
  if( key.IsEmpty() ) {
    return;
  }
  if( msging( _T( "是否确实要删除此变量?" ), _T( "审查" ), wxYES_NO | wxICON_QUESTION ) == wxID_YES ) {
    CustomVarAction Action = {CVA_Remove, key, wxEmptyString};
    m_CustomVarActions.push_back( Action );
    list->Delete( sel );
    m_bDirty = true;
  }
}

void CompilerOptionsDlg::OnClearVarClick( M_Code_unused wxCommandEvent& event ) {
  wxListBox* lstVars = XRCCTRL( *this, "lstVars", wxListBox );
  if( lstVars->IsEmpty() ) {
    return;
  }
  if( msging( _T( "是否确实要清除所有变量?" ), _T( "审查" ), wxYES | wxNO | wxICON_QUESTION ) == wxID_YES ) {
    for( size_t i = 0; i < lstVars->GetCount(); ++i ) {
      const wxString &key = static_cast<VariableListClientData*>( lstVars->GetClientObject( i ) )->key;
      if( !key.IsEmpty() ) {
        CustomVarAction Action = {CVA_Remove, key, wxEmptyString};
        m_CustomVarActions.push_back( Action );
      }
    }
    lstVars->Clear();
    m_bDirty = true;
  }
}

void CompilerOptionsDlg::OnSetDefaultCompilerClick( M_Code_unused wxCommandEvent& event ) {
  wxChoice* cmb = XRCCTRL( *this, "cmbCompiler", wxChoice );
  int idx = cmb->GetSelection();
  CompilerFactory::SetDefaultCompiler( idx );
  wxString msg;
  Compiler* compiler = CompilerFactory::GetDefaultCompiler();
  msg.Printf( _T( "%s 现在被选为新项目的默认编译器" ), compiler ? compiler->GetName().c_str() : _T( "[invalid]" ) );
  msging( msg );
}

void CompilerOptionsDlg::OnAddCompilerClick( M_Code_unused wxCommandEvent& event ) {
  if( m_bDirty ) {
    InfoWindow::Tip( _T( "更改了设置 以更新" ), _T( "提示" ), 2500 );
    DoSaveCompilerDependentSettings();
  }
  wxChoice* cmb = 0;
  cmb = XRCCTRL( *this, "cmbCompiler", wxChoice );
  wxString value = cbGetTextFromUser( _T( "请输入新编译器的名称:" ), _T( "添加新编译器" ),
                                      _T( "bak_ " ) + CompilerFactory::GetCompiler( m_CurrentCompilerIdx )->GetName() );
  if( !value.IsEmpty() ) {
    Compiler* newC = 0;
    try {
      newC = CompilerFactory::CreateCompilerCopy( CompilerFactory::GetCompiler( m_CurrentCompilerIdx ), value );
    } catch( M_Code_Exception& e ) {
      e.ShowErrorMessage( false );
      newC = 0;
    }
    if( !newC ) {
      msging( _T( "无法创建新的编译器（可能已经存在同名的编译器？）" ), _T( "错误" ), wxICON_ERROR );
      return;
    } else {
      m_CurrentCompilerIdx = CompilerFactory::GetCompilerIndex( newC );
      cmb->Append( value );
      cmb->SetSelection( cmb->GetCount() - 1 );
      DoFillCompilerDependentSettings();
      InfoWindow::Tip( _T( "新编译器已添加 别忘了更新工具链" ), _T( "提示" ), 2500 );
    }
  }
  if( m_bDirty ) {
    m_bDirty = false;
    CompilerChanged();
  }
}

void CompilerOptionsDlg::OnEditCompilerClick( M_Code_unused wxCommandEvent& event ) {
  wxChoice* cmb = XRCCTRL( *this, "cmbCompiler", wxChoice );
  wxString value = cbGetTextFromUser( _T( "请编辑编译器的名称:" ), _T( "重命名编译器" ), cmb->GetStringSelection() );
  if( !value.IsEmpty() ) {
    Compiler* compiler = CompilerFactory::GetCompiler( m_CurrentCompilerIdx );
    if( compiler ) {
      compiler->SetName( value );
    }
    cmb->SetString( m_CurrentCompilerIdx, value );
    cmb->SetSelection( m_CurrentCompilerIdx );
  }
}

void CompilerOptionsDlg::OnRemoveCompilerClick( M_Code_unused wxCommandEvent& event ) {
  if( msging( _T( "是否确实要删除此编译器?" ), _T( "审查" ), wxYES | wxNO | wxICON_QUESTION | wxNO_DEFAULT ) == wxID_YES ) {
    wxChoice* cmb = XRCCTRL( *this, "cmbCompiler", wxChoice );
    int compilerIdx = m_CurrentCompilerIdx;
    CompilerFactory::RemoveCompiler( CompilerFactory::GetCompiler( compilerIdx ) );
    cmb->Delete( compilerIdx );
    while( compilerIdx >= ( int )( cmb->GetCount() ) ) {
      --compilerIdx;
    }
    cmb->SetSelection( compilerIdx );
    m_CurrentCompilerIdx = compilerIdx;
    DoFillCompilerDependentSettings();
  }
}

void CompilerOptionsDlg::OnResetCompilerClick( M_Code_unused wxCommandEvent& event ) {
  if( msging( _T( "将此编译器的设置重置为默认值?" ), _T( "审查" ), wxYES | wxNO | wxICON_QUESTION | wxNO_DEFAULT ) == wxID_YES ) {
    Compiler* compiler = CompilerFactory::GetCompiler( m_CurrentCompilerIdx );
    if( compiler ) {
      const wxString file = _T( "/compilers/options_" ) + compiler->GetID() + _T( ".xml" );
      if( wxFileExists( ConfigManager::GetDataFolder( true ) + file )
          && wxFileExists( ConfigManager::GetDataFolder( false ) + file ) ) {
        wxRemoveFile( ConfigManager::GetDataFolder( false ) + file );
      }
      compiler->Reset();
    }
    AutoDetectCompiler();
    CompilerFactory::SaveSettings();
    DoFillCompilerDependentSettings();
  }
}
void CompilerOptionsDlg::OnAddExtraPathClick( M_Code_unused wxCommandEvent& event ) {
  EditPathDlg dlg( this, _T( "" ), _T( "" ), _T( "添加目录" ) );
  PlaceWindow( &dlg );
  if( dlg.ShowModal() == wxID_OK ) {
    wxListBox* control = XRCCTRL( *this, "lstExtraPaths", wxListBox );
    if( control ) {
      wxString path = dlg.GetPath();
      wxArrayString extraPaths;
      ListBox2ArrayString( extraPaths, control );
      if( extraPaths.Index( path ) != wxNOT_FOUND ) {
        msging( _T( "路径已在额外路径列表中!" ), _T( "警告" ), wxICON_WARNING );
      } else {
        control->Append( path );
        m_bDirty = true;
      }
    }
  }
}

void CompilerOptionsDlg::OnEditExtraPathClick( M_Code_unused wxCommandEvent& event ) {
  wxListBox* control = XRCCTRL( *this, "lstExtraPaths", wxListBox );
  if( !control || control->GetSelection() < 0 ) {
    return;
  }
  wxFileName dir( control->GetString( control->GetSelection() ) + wxFileName::GetPathSeparator() );
  wxString initial = control->GetString( control->GetSelection() );
  if( dir.DirExists() ) {
    initial = dir.GetPath( wxPATH_GET_VOLUME );
  }
  EditPathDlg dlg( this, initial, _T( "" ), _T( "Edit directory" ) );
  PlaceWindow( &dlg );
  if( dlg.ShowModal() == wxID_OK ) {
    wxString path = dlg.GetPath();
    wxArrayString extraPaths;
    ListBox2ArrayString( extraPaths, control );
    if( extraPaths.Index( path ) != wxNOT_FOUND ) {
      msging( _T( "路径已在额外路径列表中!" ), _T( "警告" ), wxICON_WARNING );
    } else {
      control->SetString( control->GetSelection(), path );
      m_bDirty = true;
    }
  }
}

void CompilerOptionsDlg::OnRemoveExtraPathClick( M_Code_unused wxCommandEvent& event ) {
  wxListBox* control = XRCCTRL( *this, "lstExtraPaths", wxListBox );
  if( !control || control->GetSelection() < 0 ) {
    return;
  }
  control->Delete( control->GetSelection() );
  m_bDirty = true;
}

void CompilerOptionsDlg::OnClearExtraPathClick( M_Code_unused wxCommandEvent& event ) {
  wxListBox* control = XRCCTRL( *this, "lstExtraPaths", wxListBox );
  if( !control || control->IsEmpty() ) {
    return;
  }
  if( msging( _T( "从列表中删除所有额外路径?" ), _T( "审查" ), wxICON_QUESTION | wxOK | wxCANCEL ) == wxID_OK ) {
    control->Clear();
    m_bDirty = true;
  }
}

void CompilerOptionsDlg::OnIgnoreAddClick( M_Code_unused wxCommandEvent& event ) {
  wxListBox*  list = XRCCTRL( *this, "lstIgnore", wxListBox );
  wxTextCtrl* text = XRCCTRL( *this, "txtIgnore", wxTextCtrl );
  wxString ignore_str = text->GetValue().Trim();
  if( ( ignore_str.Len() > 0 ) && ( list->FindString( ignore_str ) == wxNOT_FOUND ) ) {
    list->Append( ignore_str );
    m_bDirty = true;
  }
}

void CompilerOptionsDlg::OnIgnoreRemoveClick( M_Code_unused wxCommandEvent& event ) {
  wxListBox* list = XRCCTRL( *this, "lstIgnore", wxListBox );
  if( !list || list->IsEmpty() ) {
    return;
  }
  int selection = list->GetSelection();
  if( selection != wxNOT_FOUND ) {
    list->Delete( selection );
    m_bDirty = true;
  }
}

void CompilerOptionsDlg::OnMoveDirUpClick( M_Code_unused wxCommandEvent& event ) {
  wxListBox* lst = GetDirsListBox();
  wxArrayInt sels;
  if( !lst || lst->GetSelections( sels ) < 1 ) {
    return;
  }
  for( size_t i = 1; i < lst->GetCount(); ++i ) {
    if( lst->IsSelected( i ) && !lst->IsSelected( i - 1 ) ) {
      wxString dir = lst->GetString( i );
      lst->Delete( i );
      lst->InsertItems( 1, &dir, i - 1 );
      lst->SetSelection( i - 1 );
      m_bDirty = true;
    }
  }
}

void CompilerOptionsDlg::OnMoveDirDownClick( M_Code_unused wxCommandEvent& event ) {
  wxListBox* lst = GetDirsListBox();
  wxArrayInt sels;
  if( !lst || lst->GetSelections( sels ) < 1 ) {
    return;
  }
  for( size_t i = lst->GetCount() - 1; i > 0; --i ) {
    if( lst->IsSelected( i - 1 ) && !lst->IsSelected( i ) ) {
      wxString dir = lst->GetString( i - 1 );
      lst->Delete( i - 1 );
      lst->InsertItems( 1, &dir, i );
      lst->SetSelection( i );
      m_bDirty = true;
    }
  }
}

void CompilerOptionsDlg::OnMasterPathClick( M_Code_unused wxCommandEvent& event ) {
  wxString path = ChooseDirectory( this, _T( "选择目录" ), XRCCTRL( *this, "txtMasterPath", wxTextCtrl )->GetValue() );
  if( !path.IsEmpty() ) {
    XRCCTRL( *this, "txtMasterPath", wxTextCtrl )->SetValue( path );
    m_bDirty = true;
  }
}

void CompilerOptionsDlg::OnAutoDetectClick( M_Code_unused wxCommandEvent& event ) {
  AutoDetectCompiler();
}

void CompilerOptionsDlg::OnSelectProgramClick( wxCommandEvent& event ) {
  wxTextCtrl* obj = 0L;
  if( event.GetId() == XRCID( "btnCcompiler" ) ) {
    obj = XRCCTRL( *this, "txtCcompiler", wxTextCtrl );
  } else if( event.GetId() == XRCID( "btnCPPcompiler" ) ) {
    obj = XRCCTRL( *this, "txtCPPcompiler", wxTextCtrl );
  } else if( event.GetId() == XRCID( "btnLinker" ) ) {
    obj = XRCCTRL( *this, "txtLinker", wxTextCtrl );
  } else if( event.GetId() == XRCID( "btnLibLinker" ) ) {
    obj = XRCCTRL( *this, "txtLibLinker", wxTextCtrl );
  } else if( event.GetId() == XRCID( "btnMake" ) ) {
    obj = XRCCTRL( *this, "txtMake", wxTextCtrl );
  }
  if( !obj ) {
    return;
  }
  wxString file_selection = _T( "All files (*)|*" );
  wxFileDialog dlg( this, _T( "选择目录" ), XRCCTRL( *this, "txtMasterPath", wxTextCtrl )->GetValue() + _T( "/bin" ),
                    obj->GetValue(), file_selection, wxFD_OPEN | wxFD_FILE_MUST_EXIST | compatibility::wxHideReadonly );
  dlg.SetFilterIndex( 0 );
  PlaceWindow( &dlg );
  if( dlg.ShowModal() != wxID_OK ) {
    return;
  }
  wxFileName fname( dlg.GetPath() );
  obj->SetValue( fname.GetFullName() );
  m_bDirty = true;
}

void CompilerOptionsDlg::OnAdvancedClick( M_Code_unused wxCommandEvent& event ) {
  InfoWindow::Tip( _T( "编辑编译器参数会导致编译异常谨慎编辑" ), _T( "提示" ), 2500 );
}

static void UpdateUIListBoxAndButtons( wxListBox &list, wxButton &edit, wxButton &del, wxButton &clear, wxButton &copy,
                                       wxButton &up, wxButton &down ) {
  wxArrayInt selections;
  int num = list.GetSelections( selections );
  int itemCount = list.GetCount();
  bool en = ( num > 0 );
  edit.Enable( num == 1 );
  del.Enable( en );
  clear.Enable( itemCount != 0 );
  copy.Enable( en );
  if( en ) {
    int minIndex = selections.size();
    int maxIndex = 0;
    for( int index : selections ) {
      minIndex = std::min( index, minIndex );
      maxIndex = std::max( index, maxIndex );
    }
    up.Enable( minIndex > 0 );
    down.Enable( maxIndex < itemCount - 1 );
  } else {
    up.Enable( false );
    down.Enable( false );
  }
}

void CompilerOptionsDlg::OnUpdateUI( M_Code_unused wxUpdateUIEvent& event ) {
  bool en = false;
  if( xml_File_lock ) {
    wxListBox* control = GetDirsListBox();
    if( control ) {
      UpdateUIListBoxAndButtons( *control, *XRCCTRL( *this, "btnEditDir", wxButton ),
                                 *XRCCTRL( *this, "btnDelDir", wxButton ), *XRCCTRL( *this, "btnClearDir", wxButton ),
                                 *XRCCTRL( *this, "btnCopyDirs", wxButton ), *XRCCTRL( *this, "btnMoveDirUp", wxButton ),
                                 *XRCCTRL( *this, "btnMoveDirDown", wxButton ) );
    }
    if( !m_pProject ) {
      en = XRCCTRL( *this, "lstExtraPaths", wxListBox )->GetSelection() >= 0;
      XRCCTRL( *this, "btnExtraEdit", wxButton )->Enable( en );
      XRCCTRL( *this, "btnExtraDelete", wxButton )->Enable( en );
      XRCCTRL( *this, "btnExtraClear", wxButton )->Enable( XRCCTRL( *this, "lstExtraPaths", wxListBox )->GetCount() != 0 );
    }
    en = XRCCTRL( *this, "lstVars", wxListBox )->GetSelection() >= 0;
    XRCCTRL( *this, "btnEditVar", wxButton )->Enable( en );
    XRCCTRL( *this, "btnDeleteVar", wxButton )->Enable( en );
    XRCCTRL( *this, "btnClearVar", wxButton )->Enable( XRCCTRL( *this, "lstVars", wxListBox )->GetCount() != 0 );
    wxTreeCtrl* tc = XRCCTRL( *this, "tcScope", wxTreeCtrl );
    ScopeTreeData* data = ( ScopeTreeData* )tc->GetItemData( tc->GetSelection() );
    en = ( data && data->GetTarget() );
    XRCCTRL( *this, "cmbCompilerPolicy", wxChoice )->Enable( en );
    XRCCTRL( *this, "cmbLinkerPolicy", wxChoice )->Enable( en );
    XRCCTRL( *this, "cmbIncludesPolicy", wxChoice )->Enable( en );
    XRCCTRL( *this, "cmbLibDirsPolicy", wxChoice )->Enable( en );
    if( !m_pProject ) {
      en = !data;
      int idx = XRCCTRL( *this, "cmbCompiler", wxChoice )->GetSelection();
      int count = XRCCTRL( *this, "cmbCompiler", wxChoice )->GetCount();
      Compiler* compiler = CompilerFactory::GetCompiler( idx );
      XRCCTRL( *this, "btnSetDefaultCompiler", wxButton )->Enable( CompilerFactory::GetCompilerIndex( CompilerFactory::GetDefaultCompiler() ) != idx );
      XRCCTRL( *this, "btnAddCompiler", wxButton )->Enable( en );
      XRCCTRL( *this, "btnRenameCompiler", wxButton )->Enable( en && count );
      XRCCTRL( *this, "btnDelCompiler", wxButton )->Enable( en && compiler && !compiler->GetParentID().IsEmpty() );
      XRCCTRL( *this, "chkFullHtmlLog", wxCheckBox )->Enable( XRCCTRL( *this, "chkSaveHtmlLog", wxCheckBox )->IsChecked() );
      XRCCTRL( *this, "btnIgnoreRemove", wxButton )->Enable( XRCCTRL( *this, "lstIgnore", wxListBox )->GetCount() > 0 );
      XRCCTRL( *this, "btnIgnoreAdd", wxButton )->Enable( XRCCTRL( *this, "txtIgnore", wxTextCtrl )->GetValue().Trim().Len() > 0 );
    }
    wxChoice* cmb = XRCCTRL( *this, "cmbCompiler", wxChoice );
    int compilerIdx = cmb->GetSelection();
    m_CompilerId = CompilerFactory::GetCompiler( compilerIdx )->GetID();
  }
}

void CompilerOptionsDlg::OnApply() {
  m_CurrentCompilerIdx = XRCCTRL( *this, "cmbCompiler", wxChoice )->GetSelection();
  DoSaveCompilerDependentSettings();
  CompilerFactory::SaveSettings();
  if( !m_pProject ) {
    ConfigManager* cfg = Manager::Get()->GetConfigManager( _T( "compiler" ) );
    wxCheckBox* chk = XRCCTRL( *this, "chkIncludeFileCwd", wxCheckBox );
    if( chk ) {
      cfg->Write( _T( "/include_file_cwd" ), ( bool )chk->IsChecked() );
    }
    chk = XRCCTRL( *this, "chkIncludePrjCwd", wxCheckBox );
    if( chk ) {
      cfg->Write( _T( "/include_prj_cwd" ), ( bool )chk->IsChecked() );
    }
    chk = XRCCTRL( *this, "chkSkipIncludeDeps", wxCheckBox );
    if( chk ) {
      cfg->Write( _T( "/skip_include_deps" ), ( bool )chk->IsChecked() );
    }
    chk = XRCCTRL( *this, "chkSaveHtmlLog", wxCheckBox );
    if( chk ) {
      cfg->Write( _T( "/save_html_build_log" ), ( bool )chk->IsChecked() );
    }
    chk = XRCCTRL( *this, "chkFullHtmlLog", wxCheckBox );
    if( chk ) {
      cfg->Write( _T( "/save_html_build_log/full_command_line" ), ( bool )chk->IsChecked() );
    }
    wxSpinCtrl* spn = XRCCTRL( *this, "spnParallelProcesses", wxSpinCtrl );
    if( spn && ( ( ( int )spn->GetValue() ) != cfg->ReadInt( _T( "/parallel_processes" ), 2 ) ) ) {
      if( m_Compiler->IsRunning() ) {
        InfoWindow::Tip( _T( "生成时不能更改并行进程的数目! 设置被忽略" ), _T( "错误" ), 3500 );
      } else {
        cfg->Write( _T( "/parallel_processes" ), ( int )spn->GetValue() );
        m_Compiler->ReAllocProcesses();
      }
    }
    spn = XRCCTRL( *this, "spnMaxErrors", wxSpinCtrl );
    if( spn ) {
      cfg->Write( _T( "/max_reported_errors" ), ( int )spn->GetValue() );
    }
    chk = XRCCTRL( *this, "chkRebuildSeperately", wxCheckBox );
    if( chk ) {
      cfg->Write( _T( "/rebuild_seperately" ), ( bool )chk->IsChecked() );
    }
    wxListBox* lst = XRCCTRL( *this, "lstIgnore", wxListBox );
    if( lst ) {
      wxArrayString IgnoreOutput;
      ListBox2ArrayString( IgnoreOutput, lst );
      cfg->Write( _T( "/ignore_output" ), IgnoreOutput );
    }
    chk = XRCCTRL( *this, "chkNonPlatComp", wxCheckBox );
    if( chk && ( chk->IsChecked() != cfg->ReadBool( _T( "/non_plat_comp" ), false ) ) ) {
      if( m_Compiler->IsRunning() ) {
        InfoWindow::Tip( _T( "在生成时不能更改启用或禁用非平台编译器的选项! 设置被忽略" ), _T( "错误" ), 3500 );
      } else {
        cfg->Write( _T( "/non_plat_comp" ), ( bool )chk->IsChecked() );
        CompilerFactory::UnregisterCompilers();
        m_Compiler->DoRegisterCompilers();
        m_Compiler->LoadOptions();
      }
    }
  }
  m_Compiler->SaveOptions();
  m_Compiler->SetupEnvironment();
  Manager::Get()->GetMacrosManager()->Reset();
  m_bDirty = false;
}

void CompilerOptionsDlg::OnMyCharHook( wxKeyEvent& event ) {
  wxWindow* focused = wxWindow::FindFocus();
  if( !focused ) {
    event.Skip();
    return;
  }
  int keycode = event.GetKeyCode();
  int id = focused->GetId();
  int myid = 0;
  unsigned int myidx = 0;
  const wxChar* str_dirs[4] = { _T( "btnEditDir" ), _T( "btnAddDir" ), _T( "btnDelDir" ), _T( "btnClearDir" ) };
  const wxChar* str_vars[4] = { _T( "btnEditVar" ), _T( "btnAddVar" ), _T( "btnDeleteVar" ), _T( "btnClearVar" ) };
  const wxChar* str_xtra[4] = { _T( "btnExtraEdit" ), _T( "btnExtraAdd" ), _T( "btnExtraDelete" ), _T( "btnExtraClear" ) };
  if( keycode == WXK_RETURN || keycode == WXK_NUMPAD_ENTER ) {
    myidx = 0;
  } else if( keycode == WXK_INSERT || keycode == WXK_NUMPAD_INSERT ) {
    myidx = 1;
  } else if( keycode == WXK_DELETE || keycode == WXK_NUMPAD_DELETE ) {
    myidx = 2;
  } else {
    event.Skip();
    return;
  }
  if( id == XRCID( "lstIncludeDirs" ) || id == XRCID( "lstLibDirs" ) ) {
    myid =  wxXmlResource::GetXRCID( str_dirs[myidx] );
  } else if( id == XRCID( "lstVars" ) ) {
    myid =  wxXmlResource::GetXRCID( str_vars[myidx] );
  } else if( id == XRCID( "lstExtraPaths" ) ) {
    myid =  wxXmlResource::GetXRCID( str_xtra[myidx] );
  } else {
    myid = 0;
  }
  if( myid == 0 ) {
    event.Skip();
  } else {
    wxCommandEvent newevent( wxEVT_COMMAND_BUTTON_CLICKED, myid );
    this->ProcessEvent( newevent );
  }
}

int CompilerOptionsDlg::m_MenuOption = -1;

void CompilerOptionsDlg::OnFlagsPopup( wxPropertyGridEvent& event ) {
  int scroll = m_FlagsPG->GetScrollPos( wxVERTICAL );
  wxPGProperty *property = event.GetProperty();
  enum FlagsMenuOptions {
    FMO_None = -1,
    FMO_New = 0,
    FMO_Modify,
    FMO_Delete,
    FMO_COnly,
    FMO_CPPOnly,
    FMO_ExpandAll,
    FMO_CollapseAll
  };
  wxMenu* pop = new wxMenu;
  pop->Append( FMO_New, _T( "New flag..." ) );
  if( property && !property->IsCategory() ) {
    pop->Append( FMO_Modify, _T( "Modify flag..." ) );
    pop->Append( FMO_Delete, _T( "Delete flag" ) );
  }
  pop->Append( FMO_COnly, _T( "C - only flags..." ) );
  pop->Append( FMO_CPPOnly, _T( "C++ - only flags..." ) );
  pop->Append( FMO_ExpandAll, _T( "展开所有类别" ) );
  pop->Append( FMO_CollapseAll, _T( "折叠所有类别" ) );
  pop->Connect( wxEVT_COMMAND_MENU_SELECTED, ( wxObjectEventFunction )&CompilerOptionsDlg::OnFlagsPopupClick );
  m_MenuOption = FMO_None;
  m_FlagsPG->PopupMenu( pop );
  delete pop;
  if( m_MenuOption == FMO_None ) {
    return;
  }
  if( m_MenuOption == FMO_COnly ) {
    Compiler* compiler = CompilerFactory::GetCompiler( m_CurrentCompilerIdx );
    wxTextEntryDialog dlg( this, _T( "列出只在C编译期间使用的标志" ),
                           _T( "C - only flags" ), compiler->GetCOnlyFlags(), wxTextEntryDialogStyle | wxTE_MULTILINE | wxRESIZE_BORDER );
    if( dlg.GetSize().GetHeight() < 220 ) {
      dlg.SetSize( dlg.GetPosition().x, dlg.GetPosition().y - ( 220 - dlg.GetSize().GetHeight() ) / 2,
                   dlg.GetSize().GetWidth(), 220 );
    }
    PlaceWindow( &dlg );
    dlg.ShowModal();
    wxString flags = dlg.GetValue();
    flags.Replace( _T( "\n" ), _T( " " ) );
    flags.Replace( _T( "\r" ), _T( " " ) );
    flags.Replace( _T( "\t" ), _T( " " ) );
    flags = MakeUniqueString( flags, _T( " " ) );
    if( flags != compiler->GetCOnlyFlags() ) {
      compiler->SetCOnlyFlags( flags );
      m_bDirty = true;
    }
    return;
  } else if( m_MenuOption == FMO_CPPOnly ) {
    Compiler* compiler = CompilerFactory::GetCompiler( m_CurrentCompilerIdx );
    wxTextEntryDialog dlg( this, _T( "仅在C++编译期间使用的列表标志" ),
                           _T( "C++ - only flags" ), compiler->GetCPPOnlyFlags(), wxTextEntryDialogStyle | wxTE_MULTILINE | wxRESIZE_BORDER );
    if( dlg.GetSize().GetHeight() < 220 ) {
      dlg.SetSize( dlg.GetPosition().x, dlg.GetPosition().y - ( 220 - dlg.GetSize().GetHeight() ) / 2, dlg.GetSize().GetWidth(), 220 );
    }
    dlg.ShowModal();
    wxString flags = dlg.GetValue();
    flags.Replace( _T( "\n" ), _T( " " ) );
    flags.Replace( _T( "\r" ), _T( " " ) );
    flags.Replace( _T( "\t" ), _T( " " ) );
    flags = MakeUniqueString( flags, _T( " " ) );
    if( flags != compiler->GetCPPOnlyFlags() ) {
      compiler->SetCPPOnlyFlags( flags );
      m_bDirty = true;
    }
    return;
  } else if( m_MenuOption == FMO_Delete ) {
    size_t i = 0;
    for( ; i < m_Options.GetCount(); ++i ) {
      if( m_Options.GetOption( i )->name == property->GetLabel() ) {
        break;
      }
    }
    m_Options.RemoveOption( i );
  } else if( m_MenuOption == FMO_ExpandAll ) {
    m_FlagsPG->ExpandAll();
    return;
  } else if( m_MenuOption == FMO_CollapseAll ) {
    m_FlagsPG->CollapseAll();
    return;
  } else {
    wxArrayString categ;
    for( size_t i = 0; i < m_Options.GetCount(); ++i ) {
      CompOption* opt = m_Options.GetOption( i );
      bool known = false;
      for( size_t j = 0; j < categ.GetCount(); ++j ) {
        if( categ[j] == opt->category ) {
          known = true;
          break;
        }
      }
      if( !known ) {
        categ.Add( opt->category );
      }
    }
    if( categ.IsEmpty() ) {
      categ.Add( _T( "General" ) );
    }
    CompOption copt;
    if( m_MenuOption == FMO_Modify ) {
      copt = *m_Options.GetOptionByName( property->GetLabel() );
    }
    wxString categoryName;
    if( property ) {
      if( property->IsCategory() ) {
        categoryName = property->GetLabel();
      } else {
        wxPGProperty *category = property->GetParent();
        if( category ) {
          categoryName = category->GetLabel();
        }
      }
    }
    CompilerFlagDlg dlg( nullptr, &copt, categ, categoryName );
    PlaceWindow( &dlg );
    if( dlg.ShowModal() != wxID_OK ) {
      return;
    }
    if( m_MenuOption == FMO_New ) {
      size_t i;
      if( property ) {
        wxString name;
        if( property->IsCategory() ) {
          wxPGProperty *child = m_FlagsPG->GetFirstChild( property );
          if( child ) {
            name = child->GetLabel();
          }
        } else
        { name = property->GetLabel(); }
        for( i = 0; i < m_Options.GetCount(); ++i ) {
          if( m_Options.GetOption( i )->name == name ) {
            break;
          }
        }
      } else
      { i = m_Options.GetCount() - 1; }
      m_Options.AddOption( copt.name, copt.option, copt.category, copt.additionalLibs,
                           copt.checkAgainst, copt.checkMessage, copt.supersedes, copt.exclusive, i + 1 );
    } else {
      CompOption* opt = m_Options.GetOptionByName( property->GetLabel() );
      wxString name = copt.name + _T( "  [" );
      if( copt.option.IsEmpty() ) {
        name += copt.additionalLibs;
      } else
      { name += copt.option; }
      name += _T( "]" );
      opt->name = name;
      opt->option = copt.option;
      opt->additionalLibs = copt.additionalLibs;
      opt->category = copt.category;
      opt->checkAgainst = copt.checkAgainst;
      opt->checkMessage = copt.checkMessage;
      opt->supersedes = copt.supersedes;
      opt->exclusive = copt.exclusive;
    }
  }
  DoFillOptions();
  m_FlagsPG->ScrollLines( scroll );
  m_bFlagsDirty = true;
}

void CompilerOptionsDlg::OnFlagsPopupClick( wxCommandEvent& event ) {
  m_MenuOption = event.GetId();
}

void CompilerOptionsDlg::OnOptionDoubleClick( wxPropertyGridEvent& event ) {
  wxPGProperty* property = event.GetProperty();
  if( property && property->IsKindOf( CLASSINFO( wxBoolProperty ) ) ) {
    bool realValue = m_FlagsPG->GetPropertyValue( property );
    m_FlagsPG->ChangePropertyValue( property, !realValue );
  }
  event.Skip();
}
